<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  
  <meta name="renderer" content="webkit">
  <meta http-equiv="X-UA-Compatible" content="IE=edge" >
  <link rel="dns-prefetch" href="http://bzayyz.gitee.io">
  <title>前端学习 | Bzay</title>
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
  <meta name="description" content="前言">
<meta property="og:type" content="article">
<meta property="og:title" content="前端学习">
<meta property="og:url" content="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/index.html">
<meta property="og:site_name" content="Bzay">
<meta property="og:description" content="前言">
<meta property="og:locale" content="en_US">
<meta property="og:image" content="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/v2-7ae07af75faf459f780609c9710270ad_1440w.jpg">
<meta property="og:image" content="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/20210518112015646.png">
<meta property="og:image" content="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/cdn-overview.png">
<meta property="og:image" content="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220217195005390.png">
<meta property="og:image" content="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220219002243410.png">
<meta property="og:image" content="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220219002131860.png">
<meta property="og:image" content="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220219002147782.png">
<meta property="og:image" content="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220219002131860.png">
<meta property="og:image" content="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220219002147782.png">
<meta property="og:image" content="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220219002335676.png">
<meta property="og:image" content="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220219003422533.png">
<meta property="og:image" content="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220219003447878.png">
<meta property="og:image" content="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220224001826228.png">
<meta property="og:image" content="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220221214558164.png">
<meta property="og:image" content="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/1034798-20190714201939579-307479276.png">
<meta property="og:image" content="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220220131356736.png">
<meta property="og:image" content="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220220130238341.png">
<meta property="og:image" content="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220220131438722.png">
<meta property="og:image" content="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220220133311095.png">
<meta property="og:image" content="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220220134004374.png">
<meta property="og:image" content="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220220134248724.png">
<meta property="og:image" content="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220220134442696.png">
<meta property="og:image" content="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220220140756630.png">
<meta property="og:image" content="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220220142852387.png">
<meta property="og:image" content="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220220145917757.png">
<meta property="og:image" content="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220220150333447.png">
<meta property="og:image" content="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220220150320306.png">
<meta property="og:image" content="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220220150603658.png">
<meta property="og:image" content="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220220154624303.png">
<meta property="og:image" content="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220220154459994.png">
<meta property="og:image" content="http://bzayyz.gitee.io/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220220161934982.png">
<meta property="og:image" content="http://bzayyz.gitee.io/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220220203453310.png">
<meta property="og:image" content="http://bzayyz.gitee.io/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220220203242248.png">
<meta property="og:image" content="http://bzayyz.gitee.io/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220220203120080.png">
<meta property="og:image" content="http://bzayyz.gitee.io/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220220204210157.png">
<meta property="og:image" content="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220220204902150.png">
<meta property="og:image" content="http://bzayyz.gitee.io/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220220205721674.png">
<meta property="og:image" content="http://bzayyz.gitee.io/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220220210902496.png">
<meta property="og:image" content="http://bzayyz.gitee.io/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220221135239680.png">
<meta property="og:image" content="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220221232703448.png">
<meta property="og:image" content="http://bzayyz.gitee.io/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220221142243159.png">
<meta property="og:image" content="http://bzayyz.gitee.io/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220221144956189.png">
<meta property="og:image" content="http://bzayyz.gitee.io/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220221145054146.png">
<meta property="og:image" content="http://bzayyz.gitee.io/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220221215330019.png">
<meta property="og:image" content="http://bzayyz.gitee.io/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220221215422320.png">
<meta property="og:image" content="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220221232256016.png">
<meta property="og:image" content="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220221233633138.png">
<meta property="og:image" content="http://bzayyz.gitee.io/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220221154511828.png">
<meta property="og:image" content="http://bzayyz.gitee.io/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220221155547371.png">
<meta property="og:image" content="http://bzayyz.gitee.io/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220221222707415.png">
<meta property="og:image" content="http://bzayyz.gitee.io/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220221222720236.png">
<meta property="og:image" content="http://bzayyz.gitee.io/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/4fafe900-3acc-11eb-85f6-6fac77c0c9b3.png">
<meta property="og:image" content="http://bzayyz.gitee.io/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220221161915981.png">
<meta property="og:image" content="http://bzayyz.gitee.io/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220221224205075.png">
<meta property="og:image" content="http://bzayyz.gitee.io/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220221225756729.png">
<meta property="og:image" content="http://bzayyz.gitee.io/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220221230323016.png">
<meta property="og:image" content="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220222203751219.png">
<meta property="og:image" content="http://bzayyz.gitee.io/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220222204048854.png">
<meta property="og:image" content="http://bzayyz.gitee.io/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220222221123419.png">
<meta property="og:image" content="http://bzayyz.gitee.io/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220222233513540.png">
<meta property="og:image" content="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220223120503596.png">
<meta property="og:image" content="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220223223123620.png">
<meta property="og:image" content="http://bzayyz.gitee.io/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220223223221672.png">
<meta property="og:image" content="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220223224015953.png">
<meta property="og:image" content="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220223224050208.png">
<meta property="og:image" content="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220224184312276.png">
<meta property="og:image" content="http://bzayyz.gitee.io/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220225180727960.png">
<meta property="og:image" content="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220225181353394.png">
<meta property="og:image" content="http://bzayyz.gitee.io/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220225182216710.png">
<meta property="article:published_time" content="2022-02-16T11:07:50.696Z">
<meta property="article:modified_time" content="2022-02-26T09:48:57.636Z">
<meta property="article:author" content="John Doe">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/v2-7ae07af75faf459f780609c9710270ad_1440w.jpg">
  
    <link rel="alternative" href="/atom.xml" title="Bzay" type="application/atom+xml">
  
  
    <link rel="icon" href="/assets/img/header.jpg">
  
  <link rel="stylesheet" type="text/css" href="/./main.0cf68a.css">
  <style type="text/css">
  
    #container.show {
      background: linear-gradient(200deg,#a0cfe4,#e8c37e);
    }
  </style>
  

  

<meta name="generator" content="Hexo 6.0.0"></head>

<body>
  <div id="container" q-class="show:isCtnShow">
    <canvas id="anm-canvas" class="anm-canvas"></canvas>
    <div class="left-col" q-class="show:isShow">
      
<div class="overlay" style="background: #4d4d4d"></div>
<div class="intrude-less">
	<header id="header" class="inner">
		<a href="/" class="profilepic">
			<img src="/assets/img/header.jpg" class="js-avatar">
		</a>
		<hgroup>
		  <h1 class="header-author"><a href="/">bzay</a></h1>
		</hgroup>
		
		<p class="header-subtitle">Bzay&#39;s Blog</p>
		

		<nav class="header-menu">
			<ul>
			
				<li><a href="/">博客主页</a></li>
	        
				<li><a target="_blank" rel="noopener" href="http://124.222.76.228/">项目</a></li>
	        
				<li><a href="/tags/%E9%9A%8F%E7%AC%94/">随笔</a></li>
	        
			</ul>
		</nav>
		<nav class="header-smart-menu">
    		
    			
    			<a q-on="click: openSlider(e, 'innerArchive')" href="javascript:void(0)">所有文章</a>
    			
            
    			
    			<a q-on="click: openSlider(e, 'friends')" href="javascript:void(0)">友链</a>
    			
            
    			
    			<a q-on="click: openSlider(e, 'aboutme')" href="javascript:void(0)">关于我</a>
    			
            
		</nav>
		<nav class="header-nav">
			<div class="social">
				
					<a class="github" target="_blank" href="#" title="github"><i class="icon-github"></i></a>
		        
					<a class="qq" target="_blank" href="/assets/img/qq.png" title="qq"><i class="icon-qq"></i></a>
		        
					<a class="weixin" target="_blank" href="/assets/img/wechart.png" title="weixin"><i class="icon-weixin"></i></a>
		        
					<a class="mail" target="_blank" href="/liyuanzhe@mail.ustc.edu.cn" title="mail"><i class="icon-mail"></i></a>
		        
			</div>
		</nav>
	</header>		
</div>

    </div>
    <div class="mid-col" q-class="show:isShow,hide:isShow|isFalse">
      
<nav id="mobile-nav">
  	<div class="overlay js-overlay" style="background: #4d4d4d"></div>
	<div class="btnctn js-mobile-btnctn">
  		<div class="slider-trigger list" q-on="click: openSlider(e)"><i class="icon icon-sort"></i></div>
	</div>
	<div class="intrude-less">
		<header id="header" class="inner">
			<div class="profilepic">
				<img src="/assets/img/header.jpg" class="js-avatar">
			</div>
			<hgroup>
			  <h1 class="header-author js-header-author">bzay</h1>
			</hgroup>
			
			<p class="header-subtitle"><i class="icon icon-quo-left"></i>Bzay&#39;s Blog<i class="icon icon-quo-right"></i></p>
			
			
			
				
			
				
			
				
			
			
			
			<nav class="header-nav">
				<div class="social">
					
						<a class="github" target="_blank" href="#" title="github"><i class="icon-github"></i></a>
			        
						<a class="qq" target="_blank" href="/assets/img/qq.png" title="qq"><i class="icon-qq"></i></a>
			        
						<a class="weixin" target="_blank" href="/assets/img/wechart.png" title="weixin"><i class="icon-weixin"></i></a>
			        
						<a class="mail" target="_blank" href="/liyuanzhe@mail.ustc.edu.cn" title="mail"><i class="icon-mail"></i></a>
			        
				</div>
			</nav>

			<nav class="header-menu js-header-menu">
				<ul style="width: 70%">
				
				
					<li style="width: 33.333333333333336%"><a href="/">博客主页</a></li>
		        
					<li style="width: 33.333333333333336%"><a target="_blank" rel="noopener" href="http://124.222.76.228/">项目</a></li>
		        
					<li style="width: 33.333333333333336%"><a href="/tags/%E9%9A%8F%E7%AC%94/">随笔</a></li>
		        
				</ul>
			</nav>
		</header>				
	</div>
	<div class="mobile-mask" style="display:none" q-show="isShow"></div>
</nav>

      <div id="wrapper" class="body-wrap">
        <div class="menu-l">
          <div class="canvas-wrap">
            <canvas data-colors="#eaeaea" data-sectionHeight="100" data-contentId="js-content" id="myCanvas1" class="anm-canvas"></canvas>
          </div>
          <div id="js-content" class="content-ll">
            <article id="post-前端面试" class="article article-type-post " itemscope itemprop="blogPost">
  <div class="article-inner">
    
      <header class="article-header">
        
  
    <h1 class="article-title" itemprop="name">
      前端学习
    </h1>
  

        
        <a href="/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/" class="archive-article-date">
  	<time datetime="2022-02-16T11:07:50.696Z" itemprop="datePublished"><i class="icon-calendar icon"></i>2022-02-16</time>
</a>
        
      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <p>前言</p>
<span id="more"></span>

<h2 id="1-http-https"><a href="#1-http-https" class="headerlink" title="1.http,https"></a>1.http,https</h2><h4 id="1-1-基本概念"><a href="#1-1-基本概念" class="headerlink" title="1.1 基本概念:"></a><strong>1.1 基本概念:</strong></h4><p>http协议基于<strong>tcp协议</strong>，工作在应用层，是一个客户端和服务器端请求和响应的标准，用于从www服务器传输超文本到本地浏览器的传输协议。</p>
<p>https是以安全为目标的http通道，在http的基础上加入了ssl<strong>加密</strong>，主要作用就是保证网站的真实性,HTTP+SSL &#x3D; HTTPS。</p>
<h4 id="1-2-区别"><a href="#1-2-区别" class="headerlink" title="1.2 区别"></a><strong>1.2 区别</strong></h4><p>1.http的传输是未加密的，也就是明文的，https是加密的</p>
<p>2.https需要ca证书，花费较高</p>
<p>3 使用不同的链接方式，端口也不同，<strong>http一般是80端口，https是443端口</strong></p>
<h4 id="1-3-https工作过程"><a href="#1-3-https工作过程" class="headerlink" title="1.3 https工作过程"></a><strong>1.3 https工作过程</strong></h4><p><strong>HTTP+RSA+数字证书+会话秘钥</strong></p>
<p>1.客户用url访问服务器，请求建立ssl连接</p>
<p>2.服务器发送经过签名的CA证书以及服务器信息给浏览器</p>
<p>3.浏览器会用  自身本就携带的CA证书公钥，对发来的证书进行解签，查看证书里面的信息和服务器发来的信息是否一致</p>
<p>4.如果一致，就验证成功，然后会用自己支持的加密方式，用公钥加密发送给服务器，服务器收到后选择一种加密方式再次发给浏览器，以后就会以这个加密方式来进行交换数据</p>
<h4 id="1-4-https优缺点"><a href="#1-4-https优缺点" class="headerlink" title="1.4 https优缺点"></a><strong>1.4 https优缺点</strong></h4><p>优点：<strong>安全</strong>，能够验证用户和服务器身份，</p>
<p>缺点：SSL和CA证书都是<strong>需要钱</strong>的，建立连接费时，会使页面<strong>加载时间延长</strong>50%，增加10%-20%<strong>耗电</strong></p>
<h4 id="1-5-SSL协议之数据加密过程"><a href="#1-5-SSL协议之数据加密过程" class="headerlink" title="1.5 SSL协议之数据加密过程"></a><strong>1.5 SSL协议之数据加密过程</strong></h4><p><a target="_blank" rel="noopener" href="https://zhuanlan.zhihu.com/p/32513816">SSL协议之数据加密过程详解 - 知乎 (zhihu.com)</a></p>
<p>对称加密：a,b用同一把钥匙，钥匙泄露全玩完</p>
<p>非对称加密：公钥，私钥，公钥加密后，只能通过私钥解密，加密效率不如对称加密</p>
<p><strong>数字证书</strong></p>
<p>b发给a自己的公钥进行通信。但是这时c发送给a他自己的公钥，然后a用c给的公钥加密密文发送了出去，c再通过自己的私钥解密，这不就泄露信息了么？我们需要想个办法让a判断这个公钥到底是不是b发来的</p>
<p>数字证书内容大体如下：由权威机构——<strong>CA机构</strong>统一来进行发行</p>
<blockquote>
<p>签发证书的机构，a的加密算法，a所使用的Hash算法，a的公钥，证书到期时间</p>
</blockquote>
<p>防止中间有人对证书内容进行更改，有了一个<strong>数字签名</strong>的概念，就是把以上所有的内容做一个Hash操作，得到一个固定长度然后再传给a。</p>
<p><strong>整个过程</strong></p>
<ul>
<li><strong>第一步：</strong>首先，当a开启一个新的浏览器第一次去访问b的时候，会先让a安装一个<strong>数字证书</strong>，这个数字证书里包含的主要信息就是CA机构的公钥。</li>
<li><strong>第二步：</strong>b发送来了CA机构颁发给自己的数字证书，a通过第一步中已经得到的公钥解密CA用私钥加密的Hash-a(<strong>这个过程就是非对称加密</strong>)，然后再用传递过来的HASH算法生成一个Hash-b，如果Hash-a &#x3D;&#x3D;&#x3D; Hash-b就说明认证通过，确实是b发过来的。</li>
</ul>
<p>a访问b,CA机构给a  b的证书,b发公钥时把自己的证书也发过去，核对两个公钥一样，验证成功。</p>
<ol>
<li><strong>第一步</strong>：爱丽丝给出支持SSL协议版本号，一个客户端<strong>随机数</strong>(Client random，请注意这是第一个随机数)，客户端支持的加密方法等信息；</li>
<li><strong>第二步：</strong>鲍勃收到信息后，确认双方使用的加密方法，并返回数字证书，一个服务器生成的<strong>随机数</strong>(Server random，注意这是第二个随机数)等信息；</li>
<li><strong>第三步：</strong>爱丽丝确认数字证书的有效性，然后生成一个新的<strong>随机数</strong>(Premaster secret)，然后使用数字证书中的公钥，加密这个随机数，发给鲍勃。</li>
<li><strong>第四步：</strong>鲍勃使用自己的私钥，获取爱丽丝发来的<strong>随机数</strong>(即Premaster secret)；(第三、四步就是非对称加密的过程了)</li>
<li><strong>第五步：</strong>爱丽丝和鲍勃通过约定的加密方法(通常是<a href="https://link.zhihu.com/?target=https://zh.wikipedia.org/wiki/%E9%AB%98%E7%BA%A7%E5%8A%A0%E5%AF%86%E6%A0%87%E5%87%86">AES算法</a>)，使用前面三个随机数，生成<strong>对话密钥</strong>，用来加密接下来的通信内容；</li>
</ol>
<p><img src="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/v2-7ae07af75faf459f780609c9710270ad_1440w.jpg"></p>
<ul>
<li>CA机构颁发数字证书给鲍勃；</li>
<li>爱丽丝和鲍勃进行SSL握手，爱丽丝通过数字证书确定鲍勃的身份；</li>
<li>爱丽丝和鲍勃传递三个随机数，第三个随机数通过非对称加密算法进行传递；</li>
<li>爱丽丝和鲍勃通过一个对称加密算法生成一个对话密钥，加密接下来的通信内容。</li>
</ul>
<h4 id="1-6-HTTP2-0-的特性"><a href="#1-6-HTTP2-0-的特性" class="headerlink" title="1.6 HTTP2.0 的特性"></a>1.6 HTTP2.0 的特性</h4><ol>
<li><p>内容安全，应为http2.0是基于https的，天然具有安全特性，通过http2.0的特性可以<strong>避免</strong>单纯使用https的<strong>性能下降</strong></p>
</li>
<li><p>二进制格式，<strong>http1.X的解析是基于文本的</strong>，http2.0将所有的传输信息分割为更小的消息和帧，并对他们<strong>采用二进制格式编码</strong>，基于二进制可以让协议有<strong>更多的扩展性</strong>，比如引入了帧来传输数据和指令</p>
</li>
<li><p><strong>多路复用</strong>（滑动窗口），这个功能相当于是长连接的增强，每个request请求可以随机的混杂在一起，接收方可以根据request的id将request再归属到各自不同的服务端请求里面，另外多路复用中也支持了流的优先级，允许客户端告诉服务器那些内容是更优先级的资源，可以优先传输。</p>
</li>
</ol>
<h4 id="1-7-HTTP演化过程"><a href="#1-7-HTTP演化过程" class="headerlink" title="1.7 HTTP演化过程"></a>1.7 HTTP演化过程</h4><p><a target="_blank" rel="noopener" href="https://cloud.tencent.com/developer/article/1616608">从HTTP&#x2F;3的演进看web优化 - 云+社区 - 腾讯云 (tencent.com)</a></p>
<p>http&#x2F;1.0</p>
<blockquote>
<p>改进：</p>
<ul>
<li>增加了<strong>请求头和响应头</strong>，对于头部信息的利用（增加了POST&#x2F;HEAD方法、主动声明keep-alive、状态码、支持文件压缩、文件编码、用户代理、缓存机制…）</li>
</ul>
<p>问题：</p>
<ul>
<li>创建和断开tcp连接的开销大，TCP链接不能共用</li>
<li>如果共用连接需要在请求和响应头同时设置Connection: keep-alive</li>
</ul>
</blockquote>
<p>http&#x2F;1.1</p>
<blockquote>
<p>改进：</p>
<ul>
<li><p>增加了tcp持久连接和同域名连接共享（默认开启keep-alive）</p>
</li>
<li><p>增加了HOST标识（用于标识当前请求的<strong>主机名</strong>）</p>
</li>
<li><p>引入了安全策略</p>
</li>
<li><p>引入了<strong>cookie</strong>机制等</p>
</li>
</ul>
<p>问题：</p>
<ul>
<li>阻塞问题（队头阻塞，多个请求阻塞）</li>
<li>不能充分利用带宽资源</li>
</ul>
</blockquote>
<p>http&#x2F;2.0</p>
<blockquote>
<p>改进：</p>
<ul>
<li>设置请求优先级</li>
<li>多路复用</li>
<li>数据加密传输</li>
<li>添加了服务端推送能力（websocket基于1.1，未来会基于2.0）</li>
<li>头部压缩</li>
</ul>
<p>问题：</p>
<ul>
<li>单个TCP+TLS建立连接RTT耗时问题依然存在（3个RTT）</li>
</ul>
</blockquote>
<p>http&#x2F;3</p>
<blockquote>
<p>改进：</p>
<ul>
<li>优化了创建连接，减少RTT个数（0个或1个RTT，基于QUIC减少了 TCP 三次握手及 TLS 握手时间）</li>
</ul>
</blockquote>
<h4 id="1-8-http请求头有哪些？"><a href="#1-8-http请求头有哪些？" class="headerlink" title="1.8  http请求头有哪些？"></a>1.8  http请求头有哪些？</h4><p>HTTP1.0 定义了三种请求方法： GET, POST 和 HEAD 方法。</p>
<p>HTTP1.1 新增了六种请求方法：OPTIONS、PUT、PATCH、DELETE、TRACE 和 CONNECT 方法。</p>
<table>
<thead>
<tr>
<th align="left">序号</th>
<th align="left">方法</th>
<th align="left">描述</th>
</tr>
</thead>
<tbody><tr>
<td align="left">1</td>
<td align="left">GET</td>
<td align="left">请求指定的页面信息，并返回实体主体。</td>
</tr>
<tr>
<td align="left">2</td>
<td align="left">HEAD</td>
<td align="left"><strong>类似于 GET</strong> 请求，只不过返回的响应中没有具体的内容，用于<strong>获取报头</strong></td>
</tr>
<tr>
<td align="left">3</td>
<td align="left">POST</td>
<td align="left">向指定资源提交数据进行<strong>处理请求</strong>（例如<strong>提交表单或者上传文件</strong>）。数据被包含在请求体中。POST 请求可能会导致新的资源的建立和&#x2F;或已有资源的修改。</td>
</tr>
<tr>
<td align="left">4</td>
<td align="left">PUT</td>
<td align="left">从客户端向服务器传送的数据<strong>取代指定的文档的内容</strong>。</td>
</tr>
<tr>
<td align="left">5</td>
<td align="left">DELETE</td>
<td align="left">请求服务器删除指定的页面。</td>
</tr>
<tr>
<td align="left">6</td>
<td align="left">CONNECT</td>
<td align="left">HTTP&#x2F;1.1 协议中预留给能够将连接改为管道方式的代理服务器。</td>
</tr>
<tr>
<td align="left">7</td>
<td align="left">OPTIONS</td>
<td align="left">允许客户端<strong>查看服务器的性能</strong>，以及支持的请求方法</td>
</tr>
<tr>
<td align="left">8</td>
<td align="left">TRACE</td>
<td align="left"><strong>回显</strong>服务器收到的请求，主要用于<strong>测试或诊断</strong>。</td>
</tr>
<tr>
<td align="left">9</td>
<td align="left">PATCH</td>
<td align="left">是对 PUT 方法的补充，用来<strong>对已知资源进行局部更新</strong> 。</td>
</tr>
</tbody></table>
<h2 id="2-TCP-UDP"><a href="#2-TCP-UDP" class="headerlink" title="2.TCP UDP"></a>2.TCP UDP</h2><p>1 TCP是面向连接的，UDP是面向无连接的</p>
<p>2 TCP提供可靠地服务，也就是说，通过TCP连接传送的数据，无差错，不丢失，不重复，且按序到达，但是UDP则是尽最大努力交付，即不保证可靠交付，并且因为TCP可靠，面向连接，不丢失数据，因此适合大数据量的交换</p>
<p>3 TCP是面向字节流的,UDP是面向报文的，并且网络出现拥塞不会使得发送速率降低（因此会出现丢包，比如通话或者视频会议）</p>
<p>4 TCP 是一对一的，UDP是一对一，一对多</p>
<p>5 TCP的首部大小为20字节，UDP只有8字节</p>
<p>传输控制协议（TCP，Transmission Control Protocol）是一种面向连接的、可靠的、基于字节流的传输层通信协议，由IETF的RFC 793 [1] 定义。</p>
<p>TCP旨在适应支持多网络应用的分层协议层次结构。 连接到不同但互连的计算机通信网络的主计算机中的成对进程之间依靠TCP提供可靠的通信服务。TCP假设它可以从较低级别的协议获得简单的，可能不可靠的数据报服务。 原则上，TCP应该能够在从硬线连接到分组交换或电路交换网络的各种通信系统之上操作。</p>
<p>UDP 为应用程序提供了一种无需建立连接就可以发送封装的 IP 数据报的方法。RFC 768 [1]描述了 UDP。</p>
<p>Internet 的传输层有两个主要协议，互为补充。无连接的是 UDP，它除了给应用程序发送数据包功能并允许它们在所需的层次上架构自己的协议之外，几乎没有做什么特别的事情。面向连接的是 TCP，该协议几乎做了所有的事情</p>
<h2 id="3-websocket"><a href="#3-websocket" class="headerlink" title="3.websocket"></a>3.websocket</h2><h2 id="4-一个图片url访问后直接下载怎么实现？"><a href="#4-一个图片url访问后直接下载怎么实现？" class="headerlink" title="4.  一个图片url访问后直接下载怎么实现？"></a>4.  一个图片url访问后直接下载怎么实现？</h2><p>[(27条消息) 一个图片url访问后直接下载怎样实现？_水香木鱼-CSDN博客_一个图片url访问后直接下载怎样实现?](<a target="_blank" rel="noopener" href="https://blog.csdn.net/weixin_48337566/article/details/115613571#:~:text=%E4%B8%80%E4%B8%AA%E5%9B%BE%E7%89%87url%E8%AE%BF%E9%97%AE%E5%90%8E%E7%9B%B4%E6%8E%A5%E4%B8%8B%E8%BD%BD%E6%80%8E%E6%A0%B7%E5%AE%9E%E7%8E%B0%EF%BC%9F">https://blog.csdn.net/weixin_48337566/article/details/115613571#:~:text=一个图片url访问后直接下载怎样实现？</a> 请求的返回头里面，用于浏览器解析的重要参数就是OSS的API文档里面的返回http头，决定用户下载行为的参数。,顺便说下， download 的属性值是可选的，它用来指定下载文件的文件名。)</p>
<p>1.利用a标签的download属性</p>
<p>2.利用js下载，好处是可以加一些别的操作比如权限校验</p>
<p>​    方法是，动态创建一个隐藏的a标签 设置href和download属性，然后用js的dispathchEvent触发click</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_">downloadIamge</span>(<span class="params">imgsrc, name</span>) &#123;<span class="comment">//下载图片地址和图片名</span></span><br><span class="line"> </span><br><span class="line">  <span class="keyword">let</span> image = <span class="keyword">new</span> <span class="title class_">Image</span>();</span><br><span class="line"> </span><br><span class="line">   <span class="comment">// 解决跨域 Canvas 污染问题,画布污染一般出现在 图片域名与当前图片不对称产生的。</span></span><br><span class="line">   <span class="comment">//很多项目图片走的是cdn域名，会和正常项目的域名有偏差   所以进行图片裁剪会出现画布污染</span></span><br><span class="line">   <span class="comment">//图片域名——》canvas转化base64  （转化注意图片像素要和img 对应上）——》把图片赋值给一个img标签 ——》canvas裁剪图片</span></span><br><span class="line">  image.<span class="title function_">setAttribute</span>(<span class="string">&quot;crossOrigin&quot;</span>, <span class="string">&quot;anonymous&quot;</span>);</span><br><span class="line"> </span><br><span class="line">  image.<span class="property">onload</span> = <span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line"> </span><br><span class="line">    <span class="keyword">let</span> canvas = <span class="variable language_">document</span>.<span class="title function_">createElement</span>(<span class="string">&quot;canvas&quot;</span>);</span><br><span class="line"> </span><br><span class="line">    canvas.<span class="property">width</span> = image.<span class="property">width</span>;</span><br><span class="line"> </span><br><span class="line">    canvas.<span class="property">height</span> = image.<span class="property">height</span>;</span><br><span class="line"> </span><br><span class="line">    <span class="keyword">let</span> context = canvas.<span class="title function_">getContext</span>(<span class="string">&quot;2d&quot;</span>);</span><br><span class="line"> </span><br><span class="line">    context.<span class="title function_">drawImage</span>(image, <span class="number">0</span>, <span class="number">0</span>, image.<span class="property">width</span>, image.<span class="property">height</span>);</span><br><span class="line"> </span><br><span class="line">    <span class="keyword">let</span> url = canvas.<span class="title function_">toDataURL</span>(<span class="string">&quot;image/png&quot;</span>); <span class="comment">//得到图片的base64编码数据</span></span><br><span class="line"> </span><br><span class="line">    <span class="keyword">let</span> a = <span class="variable language_">document</span>.<span class="title function_">createElement</span>(<span class="string">&quot;a&quot;</span>); <span class="comment">// 生成一个a元素</span></span><br><span class="line"> </span><br><span class="line">    <span class="keyword">let</span> event = <span class="keyword">new</span> <span class="title class_">MouseEvent</span>(<span class="string">&quot;click&quot;</span>); <span class="comment">// 创建一个单击事件</span></span><br><span class="line"> </span><br><span class="line">    a.<span class="property">download</span> = name || <span class="string">&quot;photo&quot;</span>; <span class="comment">// 设置图片名称</span></span><br><span class="line"> </span><br><span class="line">    a.<span class="property">href</span> = url; <span class="comment">// 将生成的URL设置为a.href属性</span></span><br><span class="line"> </span><br><span class="line">    a.<span class="title function_">dispatchEvent</span>(event); <span class="comment">// 触发a的单击事件</span></span><br><span class="line"> </span><br><span class="line">  &#125;;</span><br><span class="line"> </span><br><span class="line">  image.<span class="property">src</span> = imgsrc;</span><br><span class="line">&#125;,</span><br></pre></td></tr></table></figure>



<h2 id="5-Bom对象方法"><a href="#5-Bom对象方法" class="headerlink" title="5. Bom对象方法"></a>5. Bom对象方法</h2><p><img src="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/20210518112015646.png" alt="在这里插入图片描述"></p>
<h2 id="6-浏览器状态码"><a href="#6-浏览器状态码" class="headerlink" title="6. 浏览器状态码"></a>6. 浏览器状态码</h2><p><a target="_blank" rel="noopener" href="https://www.runoob.com/http/http-status-codes.html">HTTP 状态码 | 菜鸟教程 (runoob.com)</a></p>
<h2 id="7-fetch发两次请求的原因"><a href="#7-fetch发两次请求的原因" class="headerlink" title="7.fetch发两次请求的原因"></a>7.fetch发两次请求的原因</h2><p>链接：<a target="_blank" rel="noopener" href="https://www.nowcoder.com/questionTerminal/77e45f2d9733454babbbe73bd30270f4">https://www.nowcoder.com/questionTerminal/77e45f2d9733454babbbe73bd30270f4</a><br>来源：牛客网</p>
<p>当使用fetch发送POST请求时，会先发送一个OPTION请求进行预检查，用来获知服务端是否允许该跨域请求，服务器确认允许之后会返回204状态码，表示允许该跨域请求，这时才发起实际的 HTTP 请求。在预检请求的返回中，服务器端也可以通知客户端，是否需要携带身份凭证（包括 Cookies 和 HTTP 认证相关数据）</p>
<h2 id="8-cookie-session-sessionStorage-localStorage"><a href="#8-cookie-session-sessionStorage-localStorage" class="headerlink" title="8. cookie session sessionStorage localStorage"></a>8. cookie session sessionStorage localStorage</h2><h5 id="cookie-session"><a href="#cookie-session" class="headerlink" title="cookie session"></a>cookie session</h5><p>Cookie机制：如果不在浏览器中设置过期时间，cookie被保存在内存中，生命周期随浏览器的关闭而结束，这种cookie简称会话cookie。如果在浏览器中设置了cookie的过期时间，cookie被保存在硬盘中，关闭浏览器后，cookie数据仍然存在，直到过期时间结束才消失。</p>
<p>Session机制：当服务器收到请求需要创建session对象时，首先会检查客户端请求中是否包含sessionid。如果有sessionid，服务器将根据该id返回对应session对象。如果客户端请求中没有sessionid，服务器会创建新的session对象，并把sessionid在本次响应中返回给客户端。通常使用cookie方式存储sessionid到客户端，在交互中浏览器按照规则将sessionid发送给服务器。</p>
<p>cookie：单个cookie保存的数据不能超过4kb；session大小没有限制。</p>
<p>应用场景：</p>
<p>cookie：</p>
<p>（1）判断用户是否登陆过网站，以便下次登录时能够实现自动登录（或者记住密码）。如果我们删除cookie，则每次登录必须重新填写登录的相关信息。</p>
<p>（2）保存上次登录的时间等信息。</p>
<p>（3）保存上次查看的页面</p>
<p>（4）浏览计数</p>
<p>session：Session用于保存每个用户的专用信息，变量的值保存在服务器端，通过SessionID来区分不同的客户。</p>
<p>（1）网上商城中的购物车</p>
<p>（2）保存用户登录信息</p>
<p>（3）将某些数据放入session中，供同一用户的不同页面使用</p>
<p>（4）防止用户非法登录</p>
<h5 id="sessionStorage-localStorage"><a href="#sessionStorage-localStorage" class="headerlink" title="sessionStorage localStorage"></a>sessionStorage localStorage</h5><p>1 生命周期：localStorage:localStorage的生命周期是永久的，关闭页面或浏览器之后localStorage中的数据也不会消失。localStorage除非主动删除数据，否则数据永远不会消失。</p>
<p>sessionStorage的生命周期是在仅在当前会话下有效。sessionStorage引入了一个“浏览器窗口”的概念，sessionStorage是在同源的窗口中始终存在的数据。只要这个浏览器窗口没有关闭，即使刷新页面或者进入同源另一个页面，数据依然存在。但是sessionStorage在关闭了浏览器窗口后就会被销毁。同时独立的打开同一个窗口同一个页面，sessionStorage也是不一样的。</p>
<p>2 .存储大小：localStorage和sessionStorage的存储数据大小一般都是：5MB</p>
<p>3、存储位置：localStorage和sessionStorage都保存在客户端，不与服务器进行交互通信。</p>
<p>WebStorage的优点：</p>
<p>（1）存储空间更大：cookie为4KB，而WebStorage是5MB；</p>
<p>（2）节省网络流量：WebStorage不会传送到服务器，存储在本地的数据可以直接获取，也不会像cookie一样每次请求都会传送到服务器，所以减少了客户端和服务器端的交互，节省了网络流量；</p>
<p>（3）对于那种只需要在用户浏览一组页面期间保存而关闭浏览器后就可以丢弃的数据，sessionStorage会非常方便；</p>
<p>（4）快速显示：有的数据存储在WebStorage上，再加上浏览器本身的缓存。获取数据时可以从本地获取会比从服务器端获取快得多，所以速度更快；</p>
<p>（5）安全性：WebStorage不会随着HTTP header发送到服务器端，所以安全性相对于cookie来说比较高一些，不会担心截获，但是仍然存在伪造问题；</p>
<h2 id="9-web-worker"><a href="#9-web-worker" class="headerlink" title="9. web worker()"></a>9. web worker()</h2><p><a target="_blank" rel="noopener" href="https://www.ruanyifeng.com/blog/2018/07/web-worker.html">Web Worker 使用教程 - 阮一峰的网络日志 (ruanyifeng.com)</a></p>
<p>众所周知，js 在浏览器中是单线程运行的；主要是为了准确操作 DOM。但单线程存在的问题是，UI 线程和 js 线程需要抢占资源，在 js 执行耗时逻辑时，容易造成页面假死，用户体验较差。</p>
<p>由此出现了异步操作，可不影响主界面的响应。如 ajax、promise 等。</p>
<p>后来 html5 开放的 <strong>web worker</strong> 可以<strong>在浏览器后台挂载新线程</strong>。它<strong>无法直接操作 DOM</strong>，无法访问 window、document、parent 对象。</p>
<p><strong>ServerWorker 是 web worker</strong> 的一种，也是挂载在浏览器后台运行的线程。主要用于<strong>代理网页请求</strong>，可<strong>缓存请求结果</strong>；可实现离线缓存功能。也拥有单独的作用域范围和运行环境</p>
<h2 id="10-CDN-带来的性能优化"><a href="#10-CDN-带来的性能优化" class="headerlink" title="10. CDN 带来的性能优化**"></a>10. CDN 带来的性能优化**</h2><p>CDN的全称是 Content Delivery Network，即内容分发网络。CDN 是构建在网络之上的内容分发网络，依靠部署在各地的边缘服务器，通过中心平台的负载均衡、内容分发、调度等功能模块，使用户就近获取所需内容，降低网络拥塞，提高用户访问响应速度和命中率。CDN 的关键技术主要有内容存储和分发技术。</p>
<h5 id="CDN的基本工作过程"><a href="#CDN的基本工作过程" class="headerlink" title="CDN的基本工作过程"></a>CDN的基本工作过程</h5><p>拿网络请求来举例子</p>
<p>1 当用户点击url,先经过本地DNS系统进行解析，如果本地DNS服务器没有响应域名的缓存，则本地DNS系统会将域名的解析权交给cname所指向的cdn专用服务器</p>
<p>2 CDN的DNS服务器会将CDN的全局负载均衡设备的IP地址返回给用户（总调度中心）</p>
<p>3 用户向CDN的全局负载均衡设备发起URL访问</p>
<p>4 CDN全局负载均衡根据用户的IP地址，以及用户请求的URL，选择一台用户所属区域的负载均衡设备，将请求发送到此设备上</p>
<p>5 区域负载均衡会选择一个最优的缓存服务器节点，返回到全局，全局会把用户的ip解析成这个服务器的节点，然后返回给用户</p>
<p>6 用户向缓存服务器发送请求，缓存服务器响应用户请求，将用户所需内容传送到用户终端，如果这台缓存服务器上并没有用户想要的内容，就会向上一级服务器请求内容，直到到网站的源服务器将内容拉到本地。</p>
<p><img src="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/cdn-overview.png" alt="CDN Overview"></p>
<h5 id="CDN的优势"><a href="#CDN的优势" class="headerlink" title="CDN的优势"></a>CDN的优势</h5><ol>
<li>CDN 节点解决了跨运营商和跨地域访问的问题，访问延时大大降低；</li>
<li>大部分请求在 CDN 边缘节点完成，CDN 起到了分流作用，减轻了源站的负载。</li>
</ol>
<h5 id="CDN-amp-静态资源"><a href="#CDN-amp-静态资源" class="headerlink" title="CDN &amp; 静态资源"></a>CDN &amp; 静态资源</h5><p>静态资源本身具有访问频率高、承接流量大的特点，因此静态资源加载速度始终是前端性能的一个非常关键的指标。CDN 是静态资源提速的重要手段。</p>
<h5 id="CDN-amp-Cookie"><a href="#CDN-amp-Cookie" class="headerlink" title="CDN &amp; Cookie"></a>CDN &amp; Cookie</h5><p>Cookie 是紧跟域名的。同一个域名下的所有请求，都会携带相同的 Cookie。</p>
<h2 id="11-JS底层"><a href="#11-JS底层" class="headerlink" title="11.JS底层"></a>11.JS底层</h2><p><a target="_blank" rel="noopener" href="https://learnku.com/articles/38201">初步理解 JavaScript 底层原理 | F2E 前端技术论坛 (learnku.com)</a></p>
<ol>
<li>浏览器是多进程，包含哪些进程（此处仅简明列举主要进程）：<br>（1）Browser 进程：浏览器的主进程（负责协调，主控），此进程只有一个<br>（2）第三方插件进程：每种类型的插件对应一个进程，仅当使用该插件时才创建<br>（3）GPU 进程：最多一个，用于 3D 绘制等<br>（4）浏览器渲染进程（浏览器内核，Renderer 进程，内部是多线程的）：默认每个 Tab 页面一个进程，互不影响</li>
</ol>
<ul>
<li>注意：在这里浏览器应该也有自己的<strong>优化机制</strong>，有时候打开多个 tab 页后，可以在 Chrome 任务管理器中看到，有些进程被合并了。<br>所以<strong>每一个 Tab 标签对应一个进程并不一定是绝对的</strong></li>
</ul>
<ol start="2">
<li>JS 的用途决定了 JS 必须是单线程的：</li>
</ol>
<p>作为浏览器脚本语言，JS 的主要用途是与用户互动，以及操作 DOM<br>        例如，假设有多个线程，一个线程在某 DOM 节点上添加内容，另一线程要删除这个节点，这时浏览器应该以哪个线程为准？<br>        所以，为了避免复杂性，从一诞生，JS 就是单线程，这已经成了这门语言的核心特征，将来也不会改变。<br>        为了利用多核 CPU 的计算能力，HTML5 提供了 Web Worker 标准，允许 JS 脚本创建多线程，但是子线程完全受主线程的控制，而且不能操作 DOM。所以，这个新的标准并没有改变 JS 单线程的本质</p>
<ol start="3">
<li><p>浏览器</p>
<p>在内核控制下各线程相互配合以保持同步，浏览器通常由以下 常驻线程 组成：<br>  （1）渲染引擎线程:        负责页面的渲染<br>  （2）JS引擎线程:          负责JS的解析和执行<br>  （3）定时触发器线程:      处理定时事件，比如setTimeout, setInterval<br>  （4）事件触发线程:        处理DOM事件<br>  （5）异步http请求线程:    处理http请求<br>注意：渲染引擎线程 和 JS 引擎线程 是不能同时进行的，渲染引擎线程在执行任务时， JS 引擎线程会被挂着，因为 JS 可以操作 DOM，与正在渲染中的 DOM 可能发生矛盾</p>
</li>
<li><p>异步执行的运行机制如下：<br>1、所有同步任务都在主线程上执行，形成一个执行栈(execution context stack).<br>2、除主线程外，还有任务队列，只要异步任务有了运行结果，就在任务队列中放置一个事件<br>3、执行栈中所有同步任务完成时，系统就会读取任务队列，看看里面有哪些事件<br> 这些事件又分别对应哪些异步任务，于是该任务结束等待，进入到执行栈执行<br>4、主线程不断重复上面的第3步</p>
</li>
</ol>
<h2 id="12-数据可视化"><a href="#12-数据可视化" class="headerlink" title="12. 数据可视化"></a>12. 数据可视化</h2><p><img src="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220217195005390.png" alt="image-20220217195005390"></p>
<h2 id="13-http头部的常见字段"><a href="#13-http头部的常见字段" class="headerlink" title="13. http头部的常见字段"></a>13. http头部的常见字段</h2><p><a target="_blank" rel="noopener" href="https://juejin.cn/post/6844903582362238989">https://juejin.cn/post/6844903582362238989</a></p>
<h2 id="14-HTTP支持的方法"><a href="#14-HTTP支持的方法" class="headerlink" title="14. HTTP支持的方法"></a>14. HTTP支持的方法</h2><p>GET, POST, HEAD, OPTIONS, PUT, DELETE, TRACE, CONNECT</p>
<p><a target="_blank" rel="noopener" href="https://blog.csdn.net/potato512/article/details/76696582">(27条消息) http请求方法（GET、POST、HEAD、OPTIONS、PUT、DELETE、TRACE、CONNECT）_番薯大佬的专栏-CSDN博客_get post trace</a></p>
<h2 id="15-HTML5"><a href="#15-HTML5" class="headerlink" title="15. HTML5"></a>15. HTML5</h2><ol>
<li>首先HTML为了更好地语义化，增加了header，footer, nav等语义化标签，</li>
<li>在表单方面，为了增强表单，为input增加了color,data,range等类型，</li>
<li>在存储方面，提供了WebStorage和离线存储，</li>
<li>在多媒体方面增加了音频和视频元素audio，vedio,</li>
<li>另外还有地理定位canvas画布，拖放，多线程编程的web worker和websocket协议</li>
<li>webworker</li>
</ol>
<h2 id="16-在地址栏里输入一个URL-到这个页面呈现出来，中间会发生什么？"><a href="#16-在地址栏里输入一个URL-到这个页面呈现出来，中间会发生什么？" class="headerlink" title="16. 在地址栏里输入一个URL,到这个页面呈现出来，中间会发生什么？"></a>16. 在地址栏里输入一个URL,到这个页面呈现出来，中间会发生什么？</h2><p>输入地址，浏览器查找域名的 IP 地址。<br>浏览器向 该 IP 地址的web 服务器发送一个 HTTP 请求，在发送请求之前浏览器和服务器建立TCP的三次握手，判断是否是HTTP缓存，如果是强制缓存且在有效期内，不再向服务器发请求，如果是HTTP协商缓存向后端发送请求且和后端服务器对比，在有效期内，服务器返回304，直接从浏览器获取数据，如果不在有效期内服务器返回200，返回新数据。<br>请求发送出去服务器返回重定向，浏览器再按照重定向的地址重新发送请求。<br>如果请求的参数有问题，服务器端返回400，如果服务器端挂了返回500。<br>如果有数据一切正常，当浏览器拿到服务器的数据之后，开始渲染页面同时获取HTML页面中图片、音频、视频、CSS、JS，在这期间获取到JS文件之后，会直接执行JS代码，阻塞浏览器渲染，因为渲染引擎和JS引擎互斥，不能同时工作，所以通常把Script标签放在body标签的底部。<br>渲染过程就是先将HTML转换成dom树，再将CSS样式转换成stylesheet，根据dom树和stylesheet创建布局树，进行回流，然后进入重绘阶段，对布局树进行分层，为每个图层生成绘制列表，再将图层分成图块，紧接着光栅化将图块转换成位图，得到节点的绝对像素，最后合成绘制生成页面发送给gpu。</p>
<blockquote>
<p>url原理—&gt;根据url得到ip地址—&gt;封装，传输，解封装—&gt;dom树，css对象模型树，渲染树—&gt;重排—&gt;重绘，资源下载</p>
</blockquote>
<h2 id="17-画一条0-5px的线"><a href="#17-画一条0-5px的线" class="headerlink" title="17. 画一条0.5px的线"></a>17. 画一条0.5px的线</h2><ol>
<li><p>直接设置0.5px</p>
</li>
<li><p>transform:scaleY(0.5) 缩放</p>
</li>
<li><p>box-shadow</p>
</li>
<li><p>svg</p>
</li>
<li><p>线性渐变linear-gradient</p>
<blockquote>
<p>可以通过直接设置宽高border为0.5px、设置box-shadow的垂直方向的偏移量为0.5px、借助线性渐变linear-gradient、使用transform: scaleY(0.5)的方法，使用SVG的方法。最后发现transfrom scale&#x2F;svg的方法兼容性和效果都是最好的，svg可以支持复杂的图形，所以<strong>在viewport是1的情况下，可以使用transform&#x2F;SVG画0.5px，而如果viewport的缩放比例不是1的话，那么直接画1px即可</strong>。</p>
</blockquote>
</li>
</ol>
<h2 id="18-Flex与两栏布局、三栏布局"><a href="#18-Flex与两栏布局、三栏布局" class="headerlink" title="18.Flex与两栏布局、三栏布局"></a>18.Flex与两栏布局、三栏布局</h2><h4 id="Flex与两栏布局、三栏布局-掘金-juejin-cn"><a href="#Flex与两栏布局、三栏布局-掘金-juejin-cn" class="headerlink" title="Flex与两栏布局、三栏布局 - 掘金 (juejin.cn)"></a><a target="_blank" rel="noopener" href="https://juejin.cn/post/6884930387164790792">Flex与两栏布局、三栏布局 - 掘金 (juejin.cn)</a></h4><h4 id="浮动-28条消息-图文详细-最通俗易懂的CSS-浮动float属性详解-Sunny王维-CSDN博客-css-float"><a href="#浮动-28条消息-图文详细-最通俗易懂的CSS-浮动float属性详解-Sunny王维-CSDN博客-css-float" class="headerlink" title="浮动(28条消息) (图文详细)最通俗易懂的CSS 浮动float属性详解_Sunny王维-CSDN博客_css float"></a>浮动<a target="_blank" rel="noopener" href="https://blog.csdn.net/qq_36595013/article/details/81810219?ops_request_misc=%7B%22request_id%22:%22164519854716780261982295%22,%22scm%22:%2220140713.130102334..%22%7D&request_id=164519854716780261982295&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-1-81810219.first_rank_v2_pc_rank_v29&utm_term=css+float&spm=1018.2226.3001.4187">(28条消息) (图文详细)最通俗易懂的CSS 浮动float属性详解_Sunny王维-CSDN博客_css float</a></h4><h4 id="30-分钟学会-Flex-布局-知乎-zhihu-com"><a href="#30-分钟学会-Flex-布局-知乎-zhihu-com" class="headerlink" title="30 分钟学会 Flex 布局 - 知乎 (zhihu.com)"></a><a target="_blank" rel="noopener" href="https://zhuanlan.zhihu.com/p/25303493">30 分钟学会 Flex 布局 - 知乎 (zhihu.com)</a></h4><p>有下面六种属性可以设置在容器上，它们分别是：</p>
<ol>
<li><p>flex-direction</p>
</li>
<li><p>flex-wrap</p>
</li>
<li><p>flex-flow</p>
</li>
<li><p>justify-content</p>
<blockquote>
<p>justify-content：定义了项目在主轴的对齐方式。</p>
<img src="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220219002243410.png" alt="image-20220219002243410" style="zoom: 50%;">
</blockquote>
</li>
<li><p>align-items</p>
<blockquote>
<p>align-items: 定义了项目在交叉轴上的对齐方式</p>
<p><img src="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220219002131860.png" alt="image-20220219002131860" style="zoom:33%;"><img src="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220219002147782.png" alt="image-20220219002147782"></p>
<p><img src="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220219002131860.png" alt="image-20220219002131860" style="zoom:33%;"><img src="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220219002147782.png" alt="image-20220219002147782"></p>
</blockquote>
</li>
<li><p>align-content</p>
<blockquote>
<p>align-content: 定义了多根轴线的对齐方式</p>
</blockquote>
</li>
</ol>
<p>子元素可以有六种属性：</p>
<ol>
<li><p>order</p>
</li>
<li><p>flex-basis</p>
</li>
<li><p>flex-grow</p>
</li>
<li><p>flex-shrink</p>
</li>
<li><p>flex</p>
</li>
<li><p>align-self</p>
<blockquote>
<p>align-self: 允许单个项目有与其他项目不一样的对齐方式</p>
<img src="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220219002335676.png" alt="image-20220219002335676" style="zoom:50%;"></blockquote>
</li>
</ol>
<h2 id="19-BFC"><a href="#19-BFC" class="headerlink" title="19. BFC"></a>19. BFC</h2><p><a target="_blank" rel="noopener" href="https://blog.csdn.net/dccose/article/details/120595030?spm=1001.2101.3001.6650.3&utm_medium=distribute.pc_relevant.none-task-blog-2~default~CTRLIST~Rate-3.pc_relevant_paycolumn_v3&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2~default~CTRLIST~Rate-3.pc_relevant_paycolumn_v3&utm_relevant_index=5">(28条消息) CSS中的BFC BFC详解 BFC可以解决的问题汇总_dccose的博客-CSDN博客_开启bfc</a></p>
<h5 id="1-BFC-定义"><a href="#1-BFC-定义" class="headerlink" title="1 BFC 定义"></a>1 BFC 定义</h5><p>　　BFC(Block formatting context)直译为”块级格式化上下文”。它是一个独立的渲染区域，只有Block-level box参与， 它规定了内部的Block-level Box如何布局，并且与这个区域外部毫不相干。 　　　</p>
<h5 id="2-BFC的生成"><a href="#2-BFC的生成" class="headerlink" title="2.BFC的生成"></a>2.BFC的生成</h5><p>上文提到BFC是一块渲染区域，那这块渲染区域到底在哪，它又是有多大，这些由生成BFC的元素决定，CSS2.1中规定满足下列CSS声明之一的元素便会生成BFC。</p>
<ul>
<li>根元素</li>
<li>float的值不为none</li>
<li>overflow的值不为visible</li>
<li>display的值为inline-block、table-cell、table-caption</li>
</ul>
<blockquote>
<p>display：table也认为可以生成BFC，其实这里的主要原因在于Table会默认生成一个匿名的table-cell，正是这个匿名的table-cell生成了BFC</p>
</blockquote>
<ul>
<li>position的值为absolute或fixed</li>
</ul>
<h5 id="3-BFC的约束规则"><a href="#3-BFC的约束规则" class="headerlink" title="3 BFC的约束规则"></a>3 BFC的约束规则</h5><p>1 内部的box会在垂直方向上一个接一个的放置</p>
<p>2 垂直方向上的距离由margin决定（属于同一个BFC的两个相邻box的margin会发生重叠（塌陷），与方向无关）</p>
<p>3 每个元素的左外边距与包含块的左边界相接触*（从左到右），即使浮动元素也是如此（这说明BFC不会超出他的包含块，而position为absolute的元素可以超出他的包含块边界）</p>
<p>4 BFC的区域不会与float的元素区域重叠</p>
<p>5 计算BFC的高度时，浮动子元素也会参与计算</p>
<p>6 BFC就是页面上的一个隔离的独立容器，容器里面的子元素不会影响到外面的元素，反之亦然</p>
<h5 id="4-BFC在布局中的应用"><a href="#4-BFC在布局中的应用" class="headerlink" title="4. BFC在布局中的应用"></a>4. BFC在布局中的应用</h5><h6 id="4-1-防止margin重叠（塌陷）"><a href="#4-1-防止margin重叠（塌陷）" class="headerlink" title="4.1 　防止margin重叠（塌陷）"></a>4.1 　防止margin重叠（塌陷）</h6><h6 id="4-2-清除内部浮动"><a href="#4-2-清除内部浮动" class="headerlink" title="4.2 清除内部浮动"></a>4.2 清除内部浮动</h6><h6 id="4-3-自适应多栏布局的"><a href="#4-3-自适应多栏布局的" class="headerlink" title="4.3 自适应多栏布局的"></a>4.3 自适应多栏布局的</h6><h2 id="20-垂直居中的方法"><a href="#20-垂直居中的方法" class="headerlink" title="20. 垂直居中的方法"></a>20. 垂直居中的方法</h2><ul>
<li><p>设定行高（line-height）</p>
</li>
<li><p>添加伪元素</p>
<blockquote>
<p>添加::before或者::after伪元素设置高度为100%，display设置成inline-block,需要对齐的元素也要是行内元素或行内块元素，然后设置vertical-align:middle，注意是两者都要设置。</p>
<img src="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220219003422533.png" alt="image-20220219003422533" style="zoom:33%;">

<img src="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220219003447878.png" alt="image-20220219003447878" style="zoom:33%;">

<img src="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220224001826228.png" alt="image-20220224001826228" style="zoom:67%;">


</blockquote>
</li>
<li><p>calc动态计算</p>
<blockquote>
<p>让要居中的div的top属性，与上方的距离是“50%的外框高度+ 50%的div高度”</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">height</span>:<span class="number">40px</span>;</span><br><span class="line"><span class="attribute">top</span>:<span class="built_in">calc</span>(<span class="number">50%</span> - <span class="number">20px</span>);</span><br></pre></td></tr></table></figure>
</blockquote>
</li>
<li><p>使用表格或假装表格</p>
</li>
<li><p>transform</p>
<blockquote>
<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">top</span>:<span class="number">50%</span>;</span><br><span class="line">   <span class="attribute">transform</span>:<span class="built_in">translateY</span>(-<span class="number">50%</span>);</span><br></pre></td></tr></table></figure>
</blockquote>
</li>
<li><p>绝对定位（margin:auto法）</p>
<blockquote>
<p>上下左右的数值都设为0，再搭配一个margin:auto</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.use-absolute</span>&#123;</span><br><span class="line">    <span class="attribute">position</span>: relative;</span><br><span class="line">    <span class="attribute">width</span>:<span class="number">200px</span>;</span><br><span class="line">    <span class="attribute">height</span>:<span class="number">150px</span>;</span><br><span class="line">   </span><br><span class="line">&#125;</span><br><span class="line"><span class="selector-class">.use-absolute</span> <span class="selector-tag">div</span>&#123;</span><br><span class="line">    <span class="attribute">position</span>: absolute;</span><br><span class="line">    <span class="attribute">width</span>:<span class="number">100px</span>;</span><br><span class="line">    <span class="attribute">height</span>:<span class="number">50px</span>;</span><br><span class="line">    <span class="attribute">top</span>:<span class="number">0</span>;</span><br><span class="line">    <span class="attribute">right</span>:<span class="number">0</span>;</span><br><span class="line">    <span class="attribute">bottom</span>:<span class="number">0</span>;</span><br><span class="line">    <span class="attribute">left</span>:<span class="number">0</span>;</span><br><span class="line">    <span class="attribute">margin</span>:auto;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>


</blockquote>
</li>
<li><p>使用Flexbox</p>
<blockquote>
<p>使用align-items</p>
</blockquote>
</li>
</ul>
<h3 id="水平居中："><a href="#水平居中：" class="headerlink" title="水平居中："></a>水平居中：</h3><p>1.inline-box+text-align:center</p>
<p>2.margin:auto</p>
<p>3.绝对定位+transform</p>
<ol start="4">
<li></li>
</ol>
<h2 id="21-visibility-x3D-hidden-opacity-x3D-0，display-none"><a href="#21-visibility-x3D-hidden-opacity-x3D-0，display-none" class="headerlink" title="21. visibility&#x3D;hidden, opacity&#x3D;0，display:none"></a>21. visibility&#x3D;hidden, opacity&#x3D;0，display:none</h2><p>opacity&#x3D;0，该元素隐藏起来了，但不会改变页面布局，并且，如果该元素已经绑定一些事件，如click事件，那么点击该区域，也能触发点击事件的visibility&#x3D;hidden，该元素隐藏起来了，但不会改变页面布局，但是不会触发该元素已经绑定的事件display&#x3D;none，把元素隐藏起来，并且会改变页面布局，可以理解成在页面中把该元素删除掉一样。</p>
<h2 id="22-position属性-比较"><a href="#22-position属性-比较" class="headerlink" title="22. position属性 比较"></a>22. position属性 比较</h2><p><strong>固定定位fixed：</strong></p>
<p>元素的位置相对于浏览器窗口是固定位置，即使窗口是滚动的它也不会移动。Fixed定位使元素的位置与文档流无关，因此不占据空间</p>
<p><strong>相对定位relative：</strong></p>
<p>如果对一个元素进行相对定位，它将出现在它所在的位置上。然后，可以通过设置垂直或水平位置，让这个元素“相对于”它的起点进行移动。 在使用相对定位时，无论是否进行移动，元素仍然占据原来的空间。因此，移动元素会导致它覆盖其它框</p>
<p><strong>绝对定位absolute：</strong></p>
<p>绝对定位的元素的位置相对于最近的已定位父元素，如果元素没有已定位的父元素，那么它的位置相对于。 absolute 定位使元素的位置与文档流无关，因此不占据空间。 absolute 定位的元素和其他元素重叠。</p>
<p>粘性定位sticky：</p>
<p>元素先按照普通文档流定位，然后相对于该元素在流中的flow root（BFC）和 containing block（最近的块级祖先元素）定位。而后，元素定位表现为在跨越特定阈值前为相对定位，之后为固定定位。</p>
<p><strong>默认定位Static：</strong></p>
<p>默认值。没有定位，元素出现在正常的流中（忽略top, bottom, left, right 或者 z-index 声明）。</p>
<p>inherit:</p>
<p>规定应该从父元素继承position 属性的值。</p>
<h2 id="23-浏览器主要组成与浏览器线程"><a href="#23-浏览器主要组成与浏览器线程" class="headerlink" title="23. 浏览器主要组成与浏览器线程"></a>23. 浏览器主要组成与浏览器线程</h2><p><img src="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220221214558164.png" alt="image-20220221214558164"></p>
<h3 id="1-浏览器基本组成"><a href="#1-浏览器基本组成" class="headerlink" title="1.浏览器基本组成"></a>1.浏览器基本组成</h3><p><a target="_blank" rel="noopener" href="https://www.cnblogs.com/111testing/p/11185676.html">浏览器渲染基本原理（一）：浏览器主要组成与浏览器线程 - 清风软件测试 - 博客园 (cnblogs.com)</a></p>
<p><img src="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/1034798-20190714201939579-307479276.png" alt="img"></p>
<p><strong>界面控件</strong> – 包括地址栏，前进后退，书签菜单等窗口上除了网页显示区域以外的部分</p>
<p><strong>浏览器引擎</strong> – 查询与操作渲染引擎的接口</p>
<p><strong>渲染引擎</strong> – 负责显示请求的内容。比如请求到HTML, 它会负责解析HTML、CSS并将结果显示到窗口中</p>
<p><strong>网络</strong> – 用于网络请求, 如HTTP请求。它包括平台无关的接口和各平台独立的实现</p>
<p><strong>UI后端</strong> – 绘制基础元件，如组合框与窗口。它提供平台无关的接口，内部使用操作系统的相应实现</p>
<p><strong>JS解释器&#x2F;js引擎</strong>— - 用于解析执行JavaScript代码</p>
<p><strong>数据存储持久层</strong> - 浏览器需要把所有数据存到硬盘上，如cookies。新的HTML5规范规定了一个完整（虽然轻量级）的浏览器中的数据库 web database</p>
<p><strong>核心是渲染引擎和js引擎</strong></p>
<h3 id="2-浏览器中的进程与线程"><a href="#2-浏览器中的进程与线程" class="headerlink" title="2.浏览器中的进程与线程"></a>2.浏览器中的进程与线程</h3><p>进程：</p>
<p><img src="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220220131356736.png" alt="image-20220220131356736"></p>
<p>线程：</p>
<ol>
<li><p>GUI 渲染线程</p>
</li>
<li><p>JavaScript引擎线程</p>
</li>
<li><p>定时触发器线程</p>
</li>
<li><p>事件触发线程</p>
</li>
<li><p>异步http请求线程</p>
</li>
</ol>
<blockquote>
<p>GUI渲染线程与JS引擎线程互斥的，是由于JavaScript是可操纵DOM的，如果在修改这些元素属性同时渲染界面（即JavaScript线程和UI线程同时运行），<strong>那么渲染线程前后获得的元素数据就可能不一致</strong>。当JavaScript引擎执行时GUI线程会被挂起，GUI更新会<strong>被保存在一个队列中等到引擎线程空闲时立即被执行</strong>。由于GUI渲染线程与JS执行线程是互斥的关系，当浏览器在执行JS程序的时候，GUI渲染线程会被保存在一个队列中，直到JS程序执行完成，才会接着执行。<strong>因此如果JS执行的时间过长，这样就会造成页面的渲染不连贯，导致页面渲染加载阻塞的感觉。</strong></p>
</blockquote>
<h3 id="3-浏览器渲染的进程视角"><a href="#3-浏览器渲染的进程视角" class="headerlink" title="3.浏览器渲染的进程视角"></a>3.浏览器渲染的进程视角</h3><p><a target="_blank" rel="noopener" href="https://cloud.tencent.com/developer/article/1629189">浏览器渲染（进程视角） - 云+社区 - 腾讯云 (tencent.com)</a></p>
<p><strong>总结</strong>：浏览器单进程-&gt;浏览器多进程   </p>
<h4 id="标签页与进程的关系"><a href="#标签页与进程的关系" class="headerlink" title="标签页与进程的关系"></a><strong>标签页与进程的关系</strong></h4><p> 相同站点的进程共用一个进程，拥有同一浏览上下文组，iframe也如此，不同站点的标签页有单独的进程。</p>
<img src="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220220130238341.png" alt="image-20220220130238341" style="zoom:33%;">

<p>同源：三个都相同，同站：协议和根域名相同</p>
<p>建立浏览上下文组的三种方式：</p>
<ul>
<li>1 a标签打开</li>
<li>2 window.open()脚本打开</li>
<li>3 iframe方式打开</li>
</ul>
<p>拥有同一上下文组的情况下：</p>
<blockquote>
<p>同源站点：子页面可以使用window.opener.document操作父页面dom和js对象</p>
<p>同站点不同源：不可以</p>
<p>不同站：不可以</p>
</blockquote>
<p>在a标签中添加<strong>rel&#x3D;noopener</strong>属性，那么和在新标签页单独打开站点是一样的，noopener这个属性是告诉与当前父业面<strong>不共用浏览上下文组</strong></p>
<p>两个站点使用同一渲染进程的两个条件</p>
<ul>
<li>两个站点必须属于同一站点</li>
<li>两个站点之间要有连接关系（同一浏览上下文组）</li>
</ul>
<hr>
<h3 id="4-浏览器渲染的线程视角："><a href="#4-浏览器渲染的线程视角：" class="headerlink" title="4.浏览器渲染的线程视角："></a>4.浏览器渲染的线程视角：</h3><p><a target="_blank" rel="noopener" href="https://cloud.tencent.com/developer/article/1631863">浏览器渲染（线程视角1） - 云+社区 - 腾讯云 (tencent.com)</a></p>
<p>[浏览器渲染（线程视角2） - 云+社区 - 腾讯云 (tencent.com)</p>
<p><a target="_blank" rel="noopener" href="https://cloud.tencent.com/developer/article/1631968"><a target="_blank" rel="noopener" href="https://cloud.tencent.com/developer/article/1623162">浏览器原理学习笔记05—浏览器中的页面渲染 - 云+社区 - 腾讯云 (tencent.com)</a></a></p>
<p><img src="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220220131438722.png" alt="image-20220220131438722"></p>
<ul>
<li>当用户在地址栏输入地址时，浏览器主进程处理输入信息（是输入内容还是请求url，如果是url，拼装协议），回车后的事件触发（before unload），启动加载状态，把url请求发送给网络进程</li>
<li>网络进程收到url请求后，开始构建请求信息，查找本地副本资源缓存，查找缓存失败准备发起请求<ul>
<li>ip地址和端口的查找（查找dns缓存，失败直接查找dns）</li>
<li>tcp连接（如果同域名请求大于6个，放入队列等待）</li>
<li>发起http请求</li>
<li>服务器处理请求返回信息</li>
<li>处理响应头（重定向等操作），如果是html发送消息给浏览器进程准备渲染进程</li>
<li>接受数据，直接发送给渲染进程（浏览器进程确认文档被提交后，会建立网络进程和渲染进程的数据通道）</li>
</ul>
</li>
<li>渲染进程收到提交文档消息后，开始不断接受网络进程数据进行渲染流程</li>
<li>渲染进程在合成阶段利用GPU进程快速生成位图，一旦所有的图块都被栅格化完成，将提交命令DrawQuad</li>
</ul>
<p>给浏览器进程</p>
<ul>
<li>浏览器接收DrawQuad命令将页面绘制到内存中然后提交到显卡的后缓冲区</li>
<li>显示器以60HZ的频率不断从显卡前缓冲区读取图像，GPU会将显卡前缓冲区和后缓冲区不断的互换，以达到显示器中读取最新的图片</li>
</ul>
<p><strong>页面渲染流水线：</strong></p>
<img src="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220220133311095.png" alt="image-20220220133311095" style="zoom: 50%;">



<p><strong>简要概括</strong></p>
<p>渲染进程要做的事情大部分由</p>
<p>（主线程， IO线程，合成线程，光栅化线程池配合网络进程，浏览器进程，GPU进程）</p>
<p>完成一帧图像的绘制</p>
<p><strong>IO线程</strong>主要负责和网络进程、浏览器主进程之间的交互将任务添加到消息队列尾部，</p>
<p><strong>主线程</strong>通过循环机不断地消费任务队列中的任务，按照渲染的时间顺序，<strong>JavaScript 运行在渲染进程主线程上</strong></p>
<p><strong>渲染流水线</strong>可分为如下几个子阶段：构建 DOM 树、样式计算、布局阶段、分层、绘制、分块、光栅化和合成。</p>
<p>GUI渲染线程：</p>
<blockquote>
<p>dom树 –&gt; css对象模型 –&gt;  二合一为渲染树(render tree)  –&gt;渲染树布局–&gt;绘制</p>
<p><strong>解析代码</strong>：HTML代码解析为DOM，CSS代码解析为CSSOM（CSS Object Model） <strong>对象合成</strong>：将DOM和CSSOM合成一棵渲染树（render tree） <strong>布局</strong>：计算出渲染树的布局（layout） <strong>绘制</strong>：将渲染树绘制到屏幕 以上四步<strong>并非严格按顺序执行</strong>，往往第一步还没完成，第二步和第三步就已经开始了。所以，会看到这种情况：网页的HTML代码还没下载完，但浏览器已经显示出内容了。</p>
</blockquote>
<h5 id="详细流程"><a href="#详细流程" class="headerlink" title="详细流程"></a>详细流程</h5><p><em><strong>1.生成DOM树</strong></em></p>
<img src="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220220134004374.png" alt="image-20220220134004374" style="zoom:33%;">

<p>注意，</p>
<blockquote>
<p>css中float使元素脱离标准流，但是没有改变结构，即dom树，只是改变了位置。</p>
</blockquote>
<blockquote>
<p><strong>HTML解析器</strong>并不是等待整个文档加载完成之后在解析结构，而是从网络进程和渲染进程建立的数据管道不断的读取解析</p>
<img src="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220220134248724.png" alt="image-20220220134248724" style="zoom: 50%;">

<p>html解析器执行过程分为三个阶段：</p>
<ul>
<li>通过分词器（词法分析）将html字节流转换为token</li>
<li>将token转换为dom需要的节点</li>
<li>将节点添加到document树</li>
</ul>
<p>token就是这玩意，不是用户验证的那个token</p>
<img src="http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220220134442696.png" alt="image-20220220134442696" style="zoom:50%;">
</blockquote>
<blockquote>
<p><strong>JS会阻塞dom生成</strong></p>
<p>当遇到js文件时</p>
<ul>
<li>当解析到js标签<script>有src属性，暂定dom解析，因为渲染进程并不知道js是否有对dom的接口操作</li>
<li>网络进程下载js文件，渲染进程必须等待js下载完成（网络耗时，js大小都是耗时点）</li>
</ul>
<p><strong>优化js脚本阻塞DOM</strong></p>
<p>js阻塞dom的耗时点在网络下载、js文件的大小，所以从网络层面，js体积优化</p>
<ul>
<li>下载加速：<strong>cdn加速</strong>，如果js没有操作dom可以使用<strong>异步加载</strong>async/defer，开启预解析线程</li>
</ul>
<p><strong>JavaScript 文件的下载过程会阻塞 DOM 解析</strong>。不过 Chrome 浏览器做了 HTML <strong>预解析</strong> 优化，当渲染引擎收到字节流后会开启一个预解析线程，用来分析 HTML 文件中包含的  JavaScript、CSS 等相关文件，预解析线程会提前下载。如果  JavaScript 文件中没有操作 DOM 相关代码，可以通过 async 或 defer 将该脚本设置为异步加载来优化：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">&gt;<span class="language-xml"><span class="tag">&lt;<span class="name">script</span> <span class="attr">async</span> <span class="attr">type</span>=<span class="string">&quot;text/javascript&quot;</span> <span class="attr">src</span>=<span class="string">&#x27;foo.js&#x27;</span>&gt;</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span></span><br><span class="line">&gt;<span class="language-xml"><span class="tag">&lt;<span class="name">script</span> <span class="attr">defer</span> <span class="attr">type</span>=<span class="string">&quot;text/javascript&quot;</span> <span class="attr">src</span>=<span class="string">&#x27;foo.js&#x27;</span>&gt;</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span></span><br></pre></td></tr></table></figure>

<p>使用 async 标志的脚本文件一旦加载完成，会立即执行；而使用 defer 标记的脚本文件，需要在 DOMContentLoaded 事件之前执行。</p>
<blockquote>
<p><a target="_blank" rel="noopener" href="https://www.jianshu.com/p/6e50122e8101">js文件异步加载async和defer详解 - 简书 (jianshu.com)</a></p>
<p>给<code>&lt;script&gt;</code>标签<strong>设置<code>defer</code>属性</strong>，将脚本文件设置为延迟加载，当浏览器遇到带有<code>defer</code>属性的<code>&lt;script&gt;</code>标签时，会<strong>再开启一个线程去下载js文件</strong>，同时<strong>继续解析HTML文档</strong>，等等<strong>HTML</strong>全部<strong>解析完毕</strong>DOM加载完成之后，再去执行加载好的js文件</p>
<p><code>async</code>属性和<code>defer</code>属性类似，也是会开启一个线程去下载js文件，但和<code>defer</code>不同的时，它会在下载完成后<strong>立刻执行</strong>，而不是会等到DOM加载完成之后再执行，所以还是有可能会造成阻塞。</p>
<p>只适用于外部js文件，也不能在js中使用document.write方法，但是对多个带有<code>async</code>的js文件，它不能像defer那样保证按顺序执行，它是哪个js文件先下载完就先执行哪个。</p>
<p>什么时候用<code>defer</code>，什么时候用<code>async</code>呢？一般来说，两者之间的选择则是看脚本之间是否有依赖关系，有依赖的话应当要保证执行顺序，应当使用<code>defer</code>没有依赖的话使用<code>async</code>，同时使用的话<code>defer</code>失效。要注意的是两者都不应该使用document.write，这个导致整个页面被清除。</p>
</blockquote>
<ul>
<li>体积减小：压缩js文件大小，去掉注释，利用es module的静态分析webpack等工具去掉无用包引入</li>
</ul>
</blockquote>
<p><em><strong>2.样式计算</strong></em></p>
<p>类似于html文档转换成dom树一样，css文件转换成浏览器可以理解的styleSheets</p>
<p>html->dom     css->styleSheets(cssom)</p>
<img src="%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220220140756630.png" alt="image-20220220140756630" style="zoom:50%;" />

<p><em><strong>3.布局阶段</strong></em></p>
<blockquote>
<p>布局就是计算出DOM树中可见元素的几何位置</p>
</blockquote>
<p><em><strong>4.复合阶段</strong></em></p>
<p>复合是关键渲染路径中的最后一步，浏览器为了提高渲染的效率才有了复合，复合就是把页面拆解成不同的图层（类似Photoshop），当页面发生视觉变化时，可能只是影响了某一个图层其它图层不需要受到影响，此时我们可以把需要变化的元素单独创建一个图层。</p>
<p>复合线程主要做了以下几点：将页面拆分多个图层进行绘制再进行复合，默认情况下如何拆分图层是由浏览器决定的，浏览器会根据规则来判断是否将页面拆分成多个图层以及把哪些元素拆分成单独的图层，它主要分析元素相互之间的影响，如果某个元素对其它元素影响非常的多，那这个元素就会被单独抽出成图层，这样这个元素发生变化后就仅对它单独进行重绘；当然我们也可以主动将某个元素提取出图层，这样让它的变化更加独立些。</p>
<ul>
<li><p><em><strong>分层</strong></em></p>
<blockquote>
<p>页面中有很多复杂的效果，例如内容溢出滚动、3D动画、定位z-index等，为了更加方便的实现这些效果，渲染引擎需要为特定的节点生成专用的图层，把图层组合起来就是一颗图层树</p>
</blockquote>
<p><em><strong>图层绘制</strong></em></p>
<blockquote>
<p>渲染引擎会创建绘制列表，进行图片绘制</p>
</blockquote>
<p>创建绘制列表->栅格化（将图块转换为位图）(使用GPU加速)</p>
<p><em><strong>合成与显示</strong></em></p>
<blockquote>
<p>浏览器接收到DrawQuad命令，从GPU内存中读取图片输出到显卡后缓冲区，显卡将后缓冲区内容交换至前缓冲区，由屏幕已60HZ的频率刷新显示图片</p>
</blockquote>
</li>
</ul>
<h4 id="css动画和js动画："><a href="#css动画和js动画：" class="headerlink" title="css动画和js动画："></a>css动画和js动画：</h4><p><a href="https://link.zhihu.com/?target=https://mrluo.life/article/detail/141/page-transition-optimization">知乎 - 安全中心 (zhihu.com)</a></p>
<p><strong>css3</strong>动画<strong>性能</strong>更好，<strong>js</strong>动画可实现的<strong>更复杂</strong></p>
<p>如果css动画只改变transform和opacity，这时整个CSS动画得以在<strong>复合线程</strong>完成（而js动画则会在<strong>主线程</strong>执行，然后触发复合线程进行下一步操作），特别注意的是如果改变transform和opacity是不会layout或者paint的，这时css3动画性能更好</p>
<p>1.功能涵盖面，js比css大</p>
<p>2.实现/重构难度不一，<strong>CSS3比js更加简单</strong>，性能跳优方向固定</p>
<p>3.对帧速表现不好的低版本浏览器，css3可以做到自然降级</p>
<p>4.css动画有<strong>天然事件</strong>支持</p>
<p>5.<strong>css3有兼容性问题</strong></p>
<h4 id="css动画为什么卡顿"><a href="#css动画为什么卡顿" class="headerlink" title="css动画为什么卡顿"></a>css动画为什么卡顿</h4><p>线程分为主线程和复合线程，主线程主要负责运行javascript和计算css元素的样式，页面的布局，将元素绘制到一个或多个位图中，将这些位图交给合成线程，也叫复合线程，复合线程负责通过GPU将位图绘制到屏幕上，通知主线程更新页面中可见或即将变成可见的部分的位图，计算出页面哪部分是可见的，计算出当你在滚动页面时哪部分是即将变成可见的，滚动页面时将相应位置的元素移动到可视区域</p>
<p>其原因就是主线程和合成线程的调度不合理</p>
<ul>
<li><p>（20+20）在使用height，width，margin,padding作为transition的值时，每1px主线程计算一次，然后交给合成线程计算，相当于1px<strong>计算两次</strong>，0-20px计算40次，会造成浏览器主线程的工作量比较重</p>
</li>
<li><p>（1+20）而如果使用transform的话，例如tranform:translate(-20px,0)到transform:translate(0,0)，主线程只需要进行<strong>一次</strong>tranform:translate(-20px,0)到transform:translate(0,0)，然后合成线程去一次将-20px转换到0px，计算20次，<strong>trasform合成器的工作无关乎主线程</strong>。这样的话，总计1+20计算。</p>
</li>
</ul>
<p>回过头来总结下，css3动画卡顿的解决方案： <strong>在使用css3 transtion做动画效果时，优先选择transform，尽量不要使用height，width，margin和padding。</strong></p>
<h4 id="为什么transform更加平滑？"><a href="#为什么transform更加平滑？" class="headerlink" title="为什么transform更加平滑？"></a><strong>为什么transform更加平滑？</strong></h4><p>提升到了合成层，不需要每次绘制</p>
<p>合成器的优点在于，其工作无关主线程，合成器线程不需要等待样式计算或者 JS 执行，这就是为什么合成器相关的动画最流畅，如果某个动画涉及到布局或者绘制的调整，就会涉及到主线程的重新计算，自然会慢很多。</p>
<h4 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h4><ul>
<li><p>生成<strong>dom树</strong></p>
</li>
<li><p>生成<strong>css Object Model</strong></p>
<p>样式计算：css文件主要来源于link引入，style插入，和行内style属性，渲染引擎解析到css时，要先将其转化为浏览器可以理解的styleSheet结构，转换过程需要经过属性值得标准化过程，和继承层叠规则计算出每个dom节点的样式，styleSheet也为之后js脚本提供操作接口</p>
</li>
<li><p><strong>布局</strong>：合成<strong>渲染树</strong>并布局，布局阶段将dom树与样式表styleSheet进行合并计算出最终展示的节点的样式和内容</p>
</li>
<li><p>分层：当界面内元素出现层叠上下文属性时，并且出现了内容溢出，需要裁剪出现滚动条时，就会提升为独立的一层，用层来优化渲染合成图片的速度</p>
</li>
<li><p>绘制：当分层树生成后，渲染引擎会创建绘制列表，绘制的过程中需要使用合成线程来完成</p>
</li>
<li><p>分块：合成线程会将图层划分为图块，视口附近的图块会优先进行合成</p>
</li>
<li><p>栅格化：图块是栅格化的最小单位，将图块生成位图的操作称为栅格化，渲染进程维护了栅格化线程池，来完成图块到位图的转换，在栅格化过程中，还用到了GPU进程来加速位图的生成，使用GPU生成位图保存在GPU内存中，这个过程为快速栅格化的过程。</p>
</li>
<li><p>发出Draw Quad指令：所有图块被光栅化转化为位图后，合成线程会生成一个绘制图块的命令DrawQuad发送给浏览器进程。</p>
</li>
<li><p>输出显示：浏览器进程将图像发送给显卡的后缓冲区，后缓冲区和前缓冲区不断的交替使用，已到达显示器60HZ的速率刷出图像</p>
</li>
</ul>
<h2 id="24-window-DOMContentLoaded和window-onload"><a href="#24-window-DOMContentLoaded和window-onload" class="headerlink" title="24. window.DOMContentLoaded和window.onload"></a>24. window.DOMContentLoaded<code>和</code>window.onload</h2><p>前者:  DOM解析完毕之后触发，这时候DOM解析完毕，JavaScript可以获取到DOM引用，</p>
<p>​            但是页面中的一些资源比如图片、视频等还没有加载完，作用同jQuery中的ready事件。</p>
<p>后者:  则是页面完全加载完毕，包括各种资源。</p>
<h2 id="25-web安全"><a href="#25-web安全" class="headerlink" title="25. web安全"></a>25. web安全</h2><p><a target="_blank" rel="noopener" href="https://cloud.tencent.com/developer/article/1623164?from=article.detail.1639628">浏览器原理学习笔记07—浏览器安全 - 云+社区 - 腾讯云 (tencent.com)</a></p>
<p><a target="_blank" rel="noopener" href="https://cloud.tencent.com/developer/article/1639628">浏览器安全（上） - 云+社区 - 腾讯云 (tencent.com)</a></p>
<p><a target="_blank" rel="noopener" href="https://cloud.tencent.com/developer/article/1644361">浏览器安全（下） - 云+社区 - 腾讯云 (tencent.com)</a></p>
<img src="%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220220142852387.png" alt="image-20220220142852387" style="zoom: 67%;" />

<blockquote>
<h3 id="安全与效率的平衡"><a href="#安全与效率的平衡" class="headerlink" title="安全与效率的平衡"></a>安全与效率的平衡</h3><p>为兼顾效率，目前的页面安全策略所做的平衡：</p>
<ul>
<li><p><strong>内容安全策略(CSP)</strong></p>
<p>最初的浏览器页面都支持引用外部 js、css 等第三方资源文件(如 <a target="_blank" rel="noopener" href="https://cloud.tencent.com/product/cdn?from=10907">CDN</a> 服务资源部署在其他域名)，但存在 XSS 安全问题，因此引入 CSP 来让服务器决定浏览器中资源的加载和内联 JavaScript 代码的执行。</p>
</li>
<li><p><strong>跨域资源共享(CORS)策略</strong></p>
<p>同源策略限制了不同源页面间使用 XMLHttpRequest 或 Fetch 无法直接进行跨域请求，大大制约生产力，因此引入 CORS 策略安全地进行跨域操作。</p>
</li>
<li><p><strong>跨文档消息机制(postMessage)</strong></p>
<p>不同源的 DOM 是不能相互操纵的，降低了效率，因此引入跨文档消息机制，通过 <code>window.postMessage</code> 的 JavaScript 接口进行不同源的 DOM 通信。（service worker）</p>
</li>
</ul>
</blockquote>
<h3 id="1-XSS攻击（Cross-Site-Scripting）跨站脚本攻击"><a href="#1-XSS攻击（Cross-Site-Scripting）跨站脚本攻击" class="headerlink" title="1.XSS攻击（Cross Site Scripting）跨站脚本攻击"></a>1.XSS攻击（Cross Site Scripting）跨站脚本攻击</h3><p>xss攻击是黑客往html/dom中注入恶意脚本，在用户浏览页面时进行如下恶意行为：</p>
<ul>
<li>修改DOM：伪造用户的登陆界面，获取用户账号密码</li>
<li>监听用户操作：监听键盘鼠标等输入设备的信息</li>
<li>窃取cookie数据：利用站点漏洞，窃取cookie来模拟用户登录、请求用户数据</li>
<li>生成广告：在页面内生成浮窗广告，影响用户体验</li>
</ul>
<h4 id="1-1-存储型XSS攻击（利用服务端的漏洞）"><a href="#1-1-存储型XSS攻击（利用服务端的漏洞）" class="headerlink" title="1.1 存储型XSS攻击（利用服务端的漏洞）"></a>1.1 存储型XSS攻击（利用服务端的漏洞）</h4><img src="%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220220145917757.png" alt="image-20220220145917757" style="zoom:67%;" />

<ul>
<li>黑客利用站点的漏洞，将一段恶意的js代码提交到网站的<a target="_blank" rel="noopener" href="https://cloud.tencent.com/solution/database?from=10680">数据库</a></li>
<li>用户向网站请求包含了恶意js的页面</li>
<li>恶意脚本通过访问cookie或获取用户表单提交的信息上传到黑客服务器</li>
<li>黑客利用客户的cookie或账号密码信息模拟登陆进行恶意行为</li>
</ul>
<h4 id="1-2-反射性XSS攻击（利用服务端的漏洞）"><a href="#1-2-反射性XSS攻击（利用服务端的漏洞）" class="headerlink" title="1.2 反射性XSS攻击（利用服务端的漏洞）"></a>1.2 反射性XSS攻击（利用服务端的漏洞）</h4><img src="%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220220150333447.png" alt="image-20220220150333447" style="zoom:67%;" />

<img src="%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220220150320306.png" alt="image-20220220150320306" style="zoom:67%;" />

<p>反射型XSS攻击过程：</p>
<ul>
<li>黑客通过各种途径散布带有恶意脚本的链接，并诱导用户点击</li>
<li>用户从黑客提供的入口点击进入</li>
<li>由于服务端过滤的漏洞，将带有恶意脚本资源返回的同时也将恶意脚本返回</li>
</ul>
<p>通过以上方式，将恶意脚本注入到了用户可访问的范围，恶意脚本便可以为所欲为。从原理图也可以看出，反射型XSS攻击和存储型XSS攻击的不同之处是 资源服务器不会存储恶意脚本，而是<strong>通过请求链路服务器过滤考虑不周全的漏洞</strong>原样返回了攻击脚本。</p>
<h4 id="1-3-DOM型XSS攻击（利用客户端漏洞）"><a href="#1-3-DOM型XSS攻击（利用客户端漏洞）" class="headerlink" title="1.3 DOM型XSS攻击（利用客户端漏洞）"></a>1.3 DOM型XSS攻击（利用客户端漏洞）</h4><img src="%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220220150603658.png" alt="image-20220220150603658" style="zoom:67%;" />

<ul>
<li>黑客在数据传输过程中进行劫持</li>
<li>将劫持的html内容进行修改</li>
<li>用户访问修改过后的html页面内容</li>
</ul>
<p>这种劫持通常是<strong>通过路由器</strong>，或者<strong>本地的恶意软件</strong>来<strong>劫持页面</strong>，此攻击手段的特点是发生资源传输过程中的劫持篡改，不涉及到资源服务器</p>
<h4 id="1-4-XSS防范措施"><a href="#1-4-XSS防范措施" class="headerlink" title="1.4 XSS防范措施"></a>1.4 XSS防范措施</h4><blockquote>
<p>存储型XSS攻击和反射型XSS攻击都是利用web服务器的漏洞，而DOM型式是在传输过程中篡改页面，无论是何种类型，它们的共同的特点是往浏览器页面中注入恶意脚本，然后通过恶意脚本将用户信息发送至黑客部署的服务器，所以要阻止XSS攻击，通过阻止恶意js脚本注入和恶意消息上报来入手</p>
</blockquote>
<ul>
<li><strong>服务端的严格校验</strong>：服务端对输入内容进行严格过滤和转码</li>
<li>实施<strong>严格的CSP（内容安全策略</strong>）：<ul>
<li>禁止向第三方域提交数据</li>
<li>限制加载第三方域js脚本</li>
<li>禁止执行内联脚本或未授权的脚本</li>
<li>上报监控，主动监控用户数据传输上报</li>
</ul>
</li>
<li>HttpOnly属性：通过使用<strong>httponly标记cookie</strong>，来<strong>限制js读取cookie</strong></li>
<li>复杂的输入校验：增强输入校验层级控制，例如短信<a target="_blank" rel="noopener" href="https://cloud.tencent.com/product/captcha?from=10680">验证码</a>，图片验证码，长度，不同类型字符整合</li>
</ul>
<h3 id="2-CSRF攻击-Cross-Site-Request-Forgery-跨站请求伪造"><a href="#2-CSRF攻击-Cross-Site-Request-Forgery-跨站请求伪造" class="headerlink" title="2 CSRF攻击(Cross Site Request Forgery)跨站请求伪造"></a>2 CSRF攻击(Cross Site Request Forgery)跨站请求伪造</h3><blockquote>
<p>CSRF攻击(Cross Site Request Forgery)：跨站请求伪造，是指黑客引诱用户打开黑客的网址，在黑客的网站中获取用户的登陆态利用第三方站点做信息获取篡改等操作。</p>
</blockquote>
<img src="%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220220154624303.png" alt="image-20220220154624303" style="zoom:67%;" />

<p>和XSS不同，CSRF攻击不需要注入恶意代码到用户界面，主要是利用服务器逻辑处理漏洞，利用有效的链接构造恶意的行为。</p>
<p>CSRF攻击的必要条件：</p>
<ul>
<li>浏览器保存用户的登陆态：用户要登陆过目标站点，并且在浏览器保持了站点的登陆状态</li>
<li>服务器逻辑漏洞：目标站点一定要存在CSRF漏洞</li>
<li>第三方站点可以读取用户登陆态：用户需要打开一个第三方站点，可以是论坛、邮箱、黑客的站点，点击黑客伪造的请求<ul>
<li>自动发起求get/post请求：黑客利用图片等障眼法发起get请求，利用隐藏表单方式将在用打开链接后，提交参数将请求参数发送至后台实现逻辑漏洞攻击</li>
<li>诱导用户点击链接：利用图片、邮箱链接等实现诱导用户点击</li>
</ul>
</li>
</ul>
<p>黑客利用浏览器中保存的登陆态、服务端漏洞，诱导客户点击第三方站点伪造的请求，实现恶意操作</p>
<h3 id="4-CSRF防范措施"><a href="#4-CSRF防范措施" class="headerlink" title="4 CSRF防范措施"></a>4 CSRF防范措施</h3><blockquote>
<p>知道了CSRF攻击的必要条件：登陆态和服务端逻辑漏洞的利用，所以防范措施也从这两点入手：</p>
</blockquote>
<ul>
<li>（登陆态防护）避免cookie被第三方站点利用：大部分网站使用了cookie来存储登陆台，保护cookie不被第三方站点利用尤为重要，合理利用cookie的samesite属性<ul>
<li>strict：严格校验，严格校验站点是否为同源</li>
<li>lax：较宽松校验，在跨站点的情况下，从第三方网站打开链接，get方式提交表单都会携带cookie，但如果在第三方站点中使用了post方法，或者通过img，iframe等标签加载的url，会禁止cookie发送</li>
<li>none：不校验第三方站点是否为同源或同一站点，任何情况下都会发送cookie</li>
</ul>
</li>
<li>（服务端加强校验）验证请求来源：在服务端验证请求源站点origin属性，请求方法，请求header等</li>
<li>（客户端来源验证）为请求打标记：<ul>
<li>服务端返回给浏览器唯一签名</li>
<li>浏览器携带签名token发送请求</li>
<li>服务端验证签名有效性，确保客户端可信</li>
</ul>
</li>
</ul>
<h2 id="26-浏览器进程安全"><a href="#26-浏览器进程安全" class="headerlink" title="26 浏览器进程安全"></a>26 浏览器进程安全</h2><h3 id="与XSS，CSRF攻击的区别"><a href="#与XSS，CSRF攻击的区别" class="headerlink" title="与XSS，CSRF攻击的区别"></a>与XSS，CSRF攻击的区别</h3><ol>
<li>XSS/CSRF攻击是利用客户端脚本或服务器漏洞，获取并利用用户在站点的身份信息，进行模拟用户身份，盗取用户信息等恶意行为，无法对操作客户端操作系统进行攻击</li>
<li>浏览器的漏洞是可以入侵到浏览器进程内部，穿透浏览器，在客户端操作系统上安装恶意软件，监听用户信息输入，盗取用户磁盘文件信息</li>
</ol>
<img src="%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220220154459994.png" alt="image-20220220154459994" style="zoom:67%;" />

<h3 id="安全沙箱"><a href="#安全沙箱" class="headerlink" title="安全沙箱"></a>安全沙箱</h3><p><strong>渲染进程和操作系统之间</strong> 建立安全的<strong>隔离机制</strong>，黑客获取渲染进程的权限无法获取之外的任何权限</p>
<blockquote>
<p>安全沙箱最小的保护单位是进程，浏览器将渲染进程封装在安全沙箱内，使得渲染进程不能直接操作缓存文件数据、网络数据、用户交互等，这些操作都会直接或间接的调用了操作系统的API，浏览器将其封装在浏览器内核中。</p>
</blockquote>
<p>安全沙箱的影响：</p>
<ul>
<li><p>使渲染进程无法直接访问文件系统,（数据读写）</p>
<p>由浏览器内核访问，然后发送给渲染进程：</p>
<blockquote>
<p>浏览器内核检查渲染进程请求url权限（如xhr、fetch的跨站请求）检查通过浏览器内核通过网络进程下载数据，通过IPC将数据传递给渲染进程</p>
</blockquote>
</li>
<li><p>使窗口不能直接被渲染进程访问，（网络访问）</p>
<p>由浏览器内核负责位图输出显示</p>
<p>渲染进程只负责渲染合成位图然后发送给浏览器内核</p>
<p>由操作系统将用户输入传递给浏览器内核（限制渲染进程直接与操作系统交互，防止监控用户输入的能力）</p>
</li>
</ul>
<h3 id="站点隔离"><a href="#站点隔离" class="headerlink" title="站点隔离"></a>站点隔离</h3><p>站点隔离就是浏览器将同一站点分配在同一渲染进程中。之所以使用站点隔离的原因如下：</p>
<ul>
<li>防止进程对操作系统的影响：处理器的架构使操作系统有两个A级漏洞（幽灵、熔毁），黑客可以通过漏洞入侵到进程内部，如果不存在安全沙箱的保护下，则可以通过进程对操作系统发起攻击</li>
<li>防止进程之间相互数据泄露：防止页面内嵌入恶意站点iframe读取当前站点渲染进程的数据，所以将不同的站点分配在不同进程中，做到内容数据隔离，互不影响。</li>
</ul>
<p><img src="/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220220161934982.png" alt="image-20220220161934982"></p>
<p>现代浏览器被划分为 <strong>浏览器内核</strong> 和 <strong>渲染内核</strong> 两个核心模块，其中浏览器内核由 <strong>网络进程</strong>、<strong>浏览器主进程</strong> 和 <a target="_blank" rel="noopener" href="https://cloud.tencent.com/product/gpu?from=10907"><strong>GPU</strong></a> <strong>进程</strong> 组成，渲染内核就是 <strong>渲染进程</strong>。浏览器内核和渲染进程间通过 <strong>IPC</strong> 通信，如网络资源先通过浏览器内核下载，再通过 IPC 提交给渲染进程；渲染进程经过解析资源、绘制等操作，将最终生成的图片通过 IPC 提交给浏览器内核模块来渲染显示。</p>
<p>渲染进程需要执行 DOM 解析、CSS 解析、网络图片解码等操作，若存在漏洞，黑客很容易利用渲染进程执行下载的恶意网络内容发起攻击。现代浏览器的 <strong>安全沙箱</strong> 利用操作系统提供的安全技术将渲染进程和操作系统隔离，渲染进程在执行过程中无法访问或修改操作系统中的数据，因此用户将无法获取渲染进程之外的任何操作权限。渲染进程对系统资源的访问则通过浏览器内核的 IPC 通信来实现。</p>
<h3 id="网络层安全：HTTP-SSL"><a href="#网络层安全：HTTP-SSL" class="headerlink" title="网络层安全：HTTP+SSL"></a>网络层安全：HTTP+SSL</h3><h2 id="26-V8引擎"><a href="#26-V8引擎" class="headerlink" title="26. V8引擎"></a>26. V8引擎</h2><h3 id="内存管理"><a href="#内存管理" class="headerlink" title="内存管理"></a>内存管理</h3><p><img src="/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220220203453310.png" alt="image-20220220203453310"></p>
<h4 id="1-内存模型"><a href="#1-内存模型" class="headerlink" title="1 内存模型"></a>1 内存模型</h4><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">foo</span>(<span class="params"></span>)&#123;</span><br><span class="line">    <span class="keyword">var</span> a = <span class="string">&quot;1&quot;</span></span><br><span class="line">    <span class="keyword">var</span> b = a</span><br><span class="line">    <span class="keyword">var</span> c = &#123;<span class="attr">name</span>:<span class="string">&quot;1&quot;</span>&#125;</span><br><span class="line">    <span class="keyword">var</span> d = c</span><br><span class="line">&#125;</span><br><span class="line"><span class="title function_">foo</span>()</span><br></pre></td></tr></table></figure>

<p><img src="/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220220203242248.png" alt="image-20220220203242248"></p>
<h4 id="2-闭包对象产生过程"><a href="#2-闭包对象产生过程" class="headerlink" title="2 闭包对象产生过程"></a>2 闭包对象产生过程</h4><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">foo</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">var</span> myName = <span class="string">&quot;1&quot;</span></span><br><span class="line">    <span class="keyword">let</span> test1 = <span class="number">1</span></span><br><span class="line">    <span class="keyword">const</span> test2 = <span class="number">2</span></span><br><span class="line">    <span class="keyword">var</span> innerBar = &#123; </span><br><span class="line">        <span class="attr">setName</span>:<span class="keyword">function</span>(<span class="params">newName</span>)&#123;</span><br><span class="line">            myName = newName</span><br><span class="line">        &#125;,</span><br><span class="line">        <span class="attr">getName</span>:<span class="keyword">function</span>(<span class="params"></span>)&#123;</span><br><span class="line">            <span class="variable language_">console</span>.<span class="title function_">log</span>(test1)</span><br><span class="line">            <span class="keyword">return</span> myName</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> innerBar</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> bar = <span class="title function_">foo</span>()</span><br><span class="line">bar.<span class="title function_">setName</span>(<span class="string">&quot;test&quot;</span>)</span><br><span class="line">bar.<span class="title function_">getName</span>()</span><br></pre></td></tr></table></figure>

<ol>
<li>foo 函数执行并创建执行上下文；</li>
<li><strong>预扫描</strong> 内部函数，编译过程中遇到内部函数如 <code>setName</code>、<code>getName</code> 则对内部函数进行 <strong>快速词法扫描</strong>，发现引用了外部函数变量如 <code>myName</code>、<code>test1</code> 则判断为闭包，在堆空间创建 <code>closure(foo)</code> 对象(内部对象 JavaScript 无法访问)来存储闭包变量如 <code>myName</code>、<code>test1</code>；</li>
<li>未被内部函数引用的变量如 <code>test2</code> 仍旧保存在调用栈中；</li>
<li>当 foo 函数退出，<code>clourse(foo)</code> 依然被其内部的 <code>getName</code> 和 <code>setName</code> 方法引用。所以在下次调用 <code>bar.setName</code> 或 <code>bar.getName</code> 时，创建的执行上下文中就包含了 <code>clourse(foo)</code>。</li>
</ol>
<p><img src="/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220220203120080.png" alt="image-20220220203120080"></p>
<h4 id="3-垃圾回收"><a href="#3-垃圾回收" class="headerlink" title="3 垃圾回收"></a>3 垃圾回收</h4><p>1.栈中</p>
<p>指针，回收直接指针下移</p>
<p><img src="/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220220204210157.png" alt="image-20220220204210157"></p>
<p>2.堆中</p>
<ul>
<li><p>使用垃圾回收器回收</p>
</li>
<li><p>分为 <strong>新生代</strong> 和 <strong>老生代</strong> 两个区域</p>
<blockquote>
<p>新生区：</p>
<p>生存时间短的对象</p>
<p>新生区通常只支持 1～8M 的容量，使用 <strong>副垃圾回收器</strong> 进行回收。    </p>
</blockquote>
</li>
<li><blockquote>
<p>老生区：</p>
<p>生存时间久的对象，支持大容量，使用<strong>主垃圾回收器</strong></p>
<p>老生区中的对象占用空间大、存活时间长，一部分来自新生区中晋升的对象，一部分来自直接分配的大对象。</p>
</blockquote>
</li>
<li><p>副垃圾回收器，使用 <strong>Scavenge 算法</strong> (空间对半划分为对象区域和空闲区域)</p>
</li>
</ul>
<img src="%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220220204902150.png" alt="image-20220220204902150" style="zoom:50%;" />

<blockquote>
<p>新对象存放到对象区域，当对象区域快满时执行一次垃圾清理，先对对象区域中的垃圾做标记，再将存活的对象有序复制到空闲区域中，相当于完成了内存整理操作。完成复制后两个角色翻转，完成了垃圾清理。</p>
<p>因为复制时间不宜过长，一般新生区空间会设置得比较小，也因此很容易填满，JavaScript 引擎采用了 <strong>对象晋升策略</strong> 来解决，即经过两次垃圾回收依然存活的对象会被移动到老生区中。</p>
</blockquote>
<ul>
<li><p>主垃圾回收器采用<strong>标记 - 清除</strong> (Mark-Sweep) 算法</p>
<blockquote>
<p>标记阶段：从<strong>调用栈</strong>根元素开始递归遍历，<strong>能到达</strong>标记为<strong>活动对象</strong>，不能到达为<strong>垃圾数据</strong></p>
<p>清除阶段：产生大量不连续的内存碎片，再使用<strong>标记 - 整理</strong> (Mark-Compact) 算法在标记后将所有存活的对象移向一端，再直接清理边界以外的内存空间。</p>
<p><img src="/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220220205721674.png" alt="image-20220220205721674"></p>
</blockquote>
</li>
<li><p>全停顿</p>
<blockquote>
<p>由于 <strong>JavaScript 运行在渲染进程主线程上</strong>，执行垃圾回收将导致暂停执行 JavaScript 脚本，即 <strong>全停顿</strong> (Stop-The-World)。为减少全停顿，V8 将标记过程分为多个的子标记过程，与 JavaScript 应用逻辑交替进行，直到标记阶段完成，称作 <strong>增量标记</strong> (Incremental Marking) 算法。</p>
</blockquote>
</li>
</ul>
<h3 id="编译器和解释器"><a href="#编译器和解释器" class="headerlink" title="编译器和解释器"></a>编译器和解释器</h3><p><img src="/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220220210902496.png" alt="image-20220220210902496"></p>
<p> **生成抽象语法树(AST)**和执行上下文，AST 是非常重要的一种数据结构，编译器或解释器依赖于 AST，而非源代码。</p>
<p>AST 在很多项目中有着广泛的应用，如：</p>
<ol>
<li>Babel 的工作原理是先将 ES6 源码转换为 AST，再将 ES6 语法的 AST 转换为 ES5 语法的 AST，最后利用 ES5 的 AST 生成 JavaScript 源代码</li>
<li>ESLint 将源码转换为 AST，再利用 AST 来检查代码规范化的问题</li>
</ol>
<blockquote>
<p>1.<strong>生成 AST</strong> </p>
</blockquote>
<p>​    需要经历分词和解析两个阶段。</p>
<ol>
<li><p>分词 / 词法分析 (tokenize)</p>
<p>将源码拆解成一个个语法上不可再分、最小的单个字符或字符串 <strong>token</strong></p>
</li>
<li><p>解析 / 语法分析 (parse)</p>
<p>将 token 根据语法规则转为 AST</p>
</li>
</ol>
<blockquote>
<p>2.生成字节码</p>
</blockquote>
<blockquote>
<p>3.生成机器码</p>
</blockquote>
<blockquote>
<p>4.执行</p>
</blockquote>
<h3 id="JavaScript-性能优化策略"><a href="#JavaScript-性能优化策略" class="headerlink" title="JavaScript 性能优化策略"></a>JavaScript 性能优化策略</h3><ol>
<li>提升单次脚本的执行速度，避免 JavaScript 的长任务霸占主线程，使得页面快速响应交互；</li>
<li>避免大的内联脚本，因为在解析 HTML 的过程中，解析和编译也会占用主线程；</li>
<li>减少 JavaScript 文件的容量，提升下载速度，并且占用更低的内存。</li>
</ol>
<h2 id="27-JS页面时间循环系统"><a href="#27-JS页面时间循环系统" class="headerlink" title="27. JS页面时间循环系统"></a>27. JS页面时间循环系统</h2><p><a target="_blank" rel="noopener" href="https://cloud.tencent.com/developer/article/1623161">浏览器原理学习笔记04—浏览器中的页面事件循环系统 - 云+社区 - 腾讯云 (tencent.com)</a></p>
<p><a target="_blank" rel="noopener" href="https://juejin.cn/post/6844903512845860872#heading-1">这一次，彻底弄懂 JavaScript 执行机制 - 掘金 (juejin.cn)</a></p>
<p><img src="/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220221135239680.png" alt="image-20220221135239680"></p>
<img src="%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220221232703448.png" alt="image-20220221232703448" style="zoom:50%;" />

<ul>
<li>同步和异步任务分别进入不同的执行"场所"，同步的进入主线程，异步的进入Event Table并注册函数。</li>
<li>当指定的事情完成时，Event Table会将这个函数移入Event Queue。</li>
<li>主线程内的任务执行完毕为空，会去Event Queue读取对应的函数，进入主线程执行。</li>
<li>上述过程会不断重复，也就是常说的Event Loop(事件循环)。</li>
</ul>
<h4 id="1-2-消息队列中的任务类型"><a href="#1-2-消息队列中的任务类型" class="headerlink" title="1.2 消息队列中的任务类型"></a>1.2 消息队列中的任务类型</h4><p><a target="_blank" rel="noopener" href="https://cs.chromium.org/chromium/src/third_party/blink/public/platform/task_type.h">Chromium 的官方源码</a> 中包含了很多内部消息类型，如输入事件(鼠标滚动、点击、移动)、微任务、文件读写、WebSocket、JavaScript 定时器等等。此外，消息队列还包含很多页面相关事件，如 JavaScript 执行、解析 DOM、样式计算、布局计算、CSS 动画等。更多事件在3、6两节中继续介绍。</p>
<p>典型的事件添加过程：</p>
<ul>
<li>当接收到 HTML 文档数据，渲染引擎会将"解析 DOM"事件添加到消息队列中</li>
<li>当用户改变 Web 页面的窗口大小，渲染引擎会将"重新布局"事件添加到消息队列中</li>
<li>当触发  JavaScript 引擎垃圾回收机制，渲染引擎会将"垃圾回收"任务添加到消息队列中</li>
<li>当要执行一段异步  JavaScript 代码，需要将执行任务添加到消息队列中</li>
<li>…...</li>
</ul>
<p>事件被添加到消息队列后，事件循环系统会按照消息队列中的顺序来执行事件。</p>
<h4 id="1-3-页面单线程缺点的优化"><a href="#1-3-页面单线程缺点的优化" class="headerlink" title="1.3 页面单线程缺点的优化"></a>1.3 页面单线程缺点的优化</h4><p>消息队列先进先出，并不太灵活。</p>
<ul>
<li><strong>高优先级任务处理优化</strong>：微任务，后续详解</li>
<li><strong>单任务执行时间过长</strong>：JavaScript 回调功能</li>
</ul>
<p><strong>消息队列优化</strong></p>
<p>高优先级队列->消息类型不同实现消息队列-->动态调度，不同阶段优先级不同->队列设置执行权重,解决饿死</p>
<h4 id="1-4-setTimeout"><a href="#1-4-setTimeout" class="headerlink" title="1.4 setTimeout"></a>1.4 setTimeout</h4><p>消息队列中的任务是按顺序执行的，为了保证 setTimeout 回调函数能在指定时间内执行，不能将定时器的回调函数直接添加到消息队列中。</p>
<p><strong>延迟队列</strong>：在 Chrome 中还有另外一个消息队列维护了需要延迟执行的任务列表，当通过 JavaScript 创建定时器时，渲染进程会将该定时器的回调任务添加到延迟队列中。</p>
<p><strong>触发方式</strong>：消息队列中的一个任务执行完成后，消息队列会根据任务发起时间和延迟时间计算出到期的任务，然后依次执行这些到期的任务。等到期的任务执行完成后，再继续下一个循环过程。</p>
<p><strong>细节</strong>:</p>
<blockquote>
<p><strong>定时器的回调函数执行时间比实际设定值延后</strong>，通过 setTimeout 设置的回调任务被放入消息队列中并不是立即执行的，要等当前任务执行完成后执行。</p>
</blockquote>
<blockquote>
<p>在<strong>定时器函数里面嵌套调用定时器</strong>，也会延长定时器的执行时间，系统会设置最短时间间隔为 4 毫秒。图中竖线是定时器的函数回调过程，前五次调用时间间隔较小，后面固定 4 毫秒。在 Chrome 中，定时器被<strong>嵌套调用 5 次以上</strong>，系统会判断该函数方法被阻塞，调用时间间隔小于 4 毫秒会设置为 4 毫秒，所以，实时性要求较高的需求，如动画，不太适合使用 setTimeout，更适合用 <code>requestAnimationFrame</code>。</p>
</blockquote>
<blockquote>
<p><strong>未激活的页面(隐藏tab)最小间隔 1000 毫秒</strong></p>
<p>目的是为了优化后台页面的加载损耗以及降低耗电量。</p>
</blockquote>
<blockquote>
<p> <strong>回调函数中的 this 不符合直觉</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> name= <span class="number">1</span>;</span><br><span class="line"><span class="keyword">var</span> <span class="title class_">MyObj</span> = &#123;</span><br><span class="line">    <span class="attr">name</span>: <span class="number">2</span>,</span><br><span class="line">    <span class="attr">showName</span>: <span class="keyword">function</span>(<span class="params"></span>)&#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="variable language_">this</span>.<span class="property">name</span>)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="title class_">MyObj</span>.<span class="property">showName</span>,<span class="number">1000</span>) <span class="comment">// 1</span></span><br></pre></td></tr></table></figure>

<p>   如上，执行上下文中的 this 会被设置为全局 window，如果是严格模式，会被设置为 undefined，解决方法：</p>
<p>   <strong>使用匿名函数</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">setTimeout</span>(<span class="function">() =&gt;</span> &#123; <span class="title class_">MyObj</span>.<span class="title function_">showName</span>() &#125;, <span class="number">1000</span>)</span><br><span class="line"><span class="comment">// 或</span></span><br><span class="line"><span class="built_in">setTimeout</span>( <span class="keyword">function</span>(<span class="params"></span>) &#123; <span class="title class_">MyObj</span>.<span class="title function_">showName</span>() &#125;, <span class="number">1000</span>)</span><br></pre></td></tr></table></figure>

<p>   <strong>使用 bind 方法</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">setTimeout</span>(<span class="title class_">MyObj</span>.<span class="property">showName</span>.<span class="title function_">bind</span>(<span class="title class_">MyObj</span>), <span class="number">1000</span>) <span class="comment">// 将 showName 绑在 MyObj 上</span></span><br></pre></td></tr></table></figure>
</blockquote>
<p>对于<code>setInterval(fn,ms)</code>来说，我们已经知道不是每过<code>ms</code>秒会执行一次<code>fn</code>，而是每过<code>ms</code>秒，会有<code>fn</code>进入Event Queue。一旦**<code>setInterval</code>的回调函数<code>fn</code>执行时间超过了延迟时间<code>ms</code>，那么就完全看不出来有时间间隔了**。</p>
<h3 id="1-5-XMLHttpRequest"><a href="#1-5-XMLHttpRequest" class="headerlink" title="1.5 XMLHttpRequest"></a>1.5 XMLHttpRequest</h3><p>和 setTimeout 直接将延迟任务添加到延迟队列中不同，XMLHttpRequest 发起请求，由浏览器的其他进程或线程执行，再将执行结果使用 IPC 的方式通知渲染进程，并将对应的消息添加到消息队列中。</p>
<p><img src="/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220221142243159.png" alt="image-20220221142243159"></p>
<h3 id="宏任务和微任务"><a href="#宏任务和微任务" class="headerlink" title="宏任务和微任务"></a>宏任务和微任务</h3><h3 id="1-宏任务-粗时间粒度"><a href="#1-宏任务-粗时间粒度" class="headerlink" title="1 宏任务(粗时间粒度)"></a>1 宏任务(粗时间粒度)</h3><p>页面中的大部分任务都是在主线程上执行的，如：</p>
<ul>
<li>渲染事件(如解析 DOM、计算布局、绘制等)</li>
<li>用户交互事件(如鼠标点击、滚动页面、放大缩小等)</li>
<li>JavaScript 脚本执行事件</li>
<li>网络请求完成、文件读写完成等事件</li>
</ul>
<p>页面进程引入消息队列和事件循环机制来协调这些任务有条不紊地执行，渲染进程内部会维护多个消息队列，如 <strong>延迟执行队列</strong> 和 <strong>普通消息队列</strong>，然后主线程采用一个 for 循环，不断地从多个消息队列中选出一个最老的任务 oldestTask 设置为当前任务，执行完成后从消息队列中删除并统计时长等信息，这些消息队列中的任务称为宏任务。但宏任务难以胜任对时间精度要求高的事件，例如两个连续 setTimeout 延迟时间为 0 的任务，中间却插入了其他系统级任务：</p>
<p><img src="/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220221144956189.png" alt="image-20220221144956189"></p>
<h3 id="2-微任务"><a href="#2-微任务" class="headerlink" title="2 微任务"></a>2 微任务</h3><p>微任务是一个异步执行的函数，执行时机是在主函数执行结束之后、当前宏任务结束之前。<strong>微任务是实时性和效率之间的一个有效的权衡</strong>。</p>
<h4 id="3-2-1-微任务队列"><a href="#3-2-1-微任务队列" class="headerlink" title="3.2.1 微任务队列"></a>3.2.1 微任务队列</h4><p>当 JavaScript 执行一段脚本时，V8 创建全局执行上下文的同时会创建一个 <strong>微任务队列</strong>，存放当前宏任务执行过程中产生的微任务。但这个微任务队只给 V8 引擎内部使用，无法通过 JavaScript 直接访问。</p>
<h4 id="3-2-2-微任务的产生"><a href="#3-2-2-微任务的产生" class="headerlink" title="3.2.2 微任务的产生"></a>3.2.2 微任务的产生</h4><p>在现代浏览器中产生微任务有两种方式：</p>
<ul>
<li>使用 <code>MutationObserver</code> 监控某个 DOM 节点，再通过 JavaScript 修改这个节点及其子节点，当 DOM 节点发生变化时产生 DOM 变化记录的微任务</li>
<li>使用 <code>Promise</code>，调用 Promise.resolve() 或 Promise.reject() 产生微任务</li>
<li>vue中使用nextTick()</li>
</ul>
<blockquote>
<p><strong>关于 MutationObserver:</strong> 最早浏览器不支持 DOM 监听，轮询方式难以平衡性能和实时性问题，2000年引入的 Mutation Event 采用同步回调监听 DOM 变化，但存在严重性能问题。MutationObserver 将其改成异步调用，使用一个能记录多次 DOM 变化记录的数据结构，<strong>一次性触发异步调用</strong>，为保证实时性不能使用 setTimeout 创建宏任务触发回调，渲染引擎将变化记录封装成微任务添加进当前任务的微任务队列中。<strong>异步 + 微任务</strong> 策略解决了性能和实时性问题。</p>
</blockquote>
<h4 id="3-2-3-微任务的执行"><a href="#3-2-3-微任务的执行" class="headerlink" title="3.2.3 微任务的执行"></a>3.2.3 微任务的执行</h4><p>在当前宏任务中的 JavaScript 快执行完成时(JavaScript 引擎准备退出全局执行上下文并清空调用栈时)，JavaScript 引擎会检查全局执行上下文中的微任务队列并按序执行。若执行微任务过程中产生了新的微任务，会添加到微任务队列中，直到队列为空才执行结束，因此不会推迟到下个宏任务。</p>
<blockquote>
<p><strong>关于检查点:</strong> 除了退出，还有其他检查点，但不重要</p>
</blockquote>
<p><img src="/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220221145054146.png" alt="image-20220221145054146"></p>
<p>上图执行一个 <code>ParseHTML</code> 宏任务过程中遇到 JavaScript 脚本，暂停解析流程并进入到 JavaScript 执行环境，添加微任务并在 JavaScript 执行结束时取出执行，可以得到结论：</p>
<ul>
<li>每个宏任务关联一个微任务队列</li>
<li>微任务的执行时长会影响当前宏任务的时长</li>
<li>在一个宏任务中分别创建一个用于回调的宏任务和微任务，微任务会早于宏任务执行</li>
</ul>
<h3 id="3-总结"><a href="#3-总结" class="headerlink" title="3 总结"></a>3 总结</h3><p><strong>宏任务：为了<em>性能</em>，维护任务队列，主线程+任务队列。带来问题：Promise任务得到以后不能立即触发then，要放到消息队列里等待下次循环才能调用，降低了实时性。</strong></p>
<p><strong>引入微任务：有一个微任务队列。当前任务产生的新回调任务放在微任务队列里，执行完主任务就执行自己的微任务，不用把新任务放在宏任务队列里。提高<em>实时性</em>。</strong></p>
<p>宏任务加微任务很好的平衡了性能和实时性。</p>
<p>宏任务：性能</p>
<p>微任务：实时性</p>
<p><img src="/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220221215330019.png" alt="image-20220221215330019"></p>
<p><img src="/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220221215422320.png" alt="image-20220221215422320"></p>
<img src="%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220221232256016.png" alt="image-20220221232256016" style="zoom:50%;" />

<img src="%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220221233633138.png" alt="image-20220221233633138" style="zoom:67%;" />

<p><a target="_blank" rel="noopener" href="https://juejin.cn/post/6844903512845860872#heading-1">这一次，彻底弄懂 JavaScript 执行机制 - 掘金 (juejin.cn)</a></p>
<h2 id="28-Promise"><a href="#28-Promise" class="headerlink" title="28. Promise"></a>28. Promise</h2><p><a target="_blank" rel="noopener" href="https://cloud.tencent.com/developer/article/1623161">浏览器原理学习笔记04—浏览器中的页面事件循环系统 - 云+社区 - 腾讯云 (tencent.com)</a></p>
<p><a target="_blank" rel="noopener" href="https://cloud.tencent.com/developer/article/1448150">从 Promise 对象讲解事件循环机制 - 云+社区 - 腾讯云 (tencent.com)</a></p>
<p><strong>产生背景</strong></p>
<blockquote>
<p>太多的回调嵌套很容易造成 <strong>回调地狱</strong>，代码凌乱的形成原因：</p>
<ul>
<li>嵌套回调，层层依赖</li>
<li>任务的不确定性，每个任务都必须处理成功和失败回调</li>
</ul>
</blockquote>
<ul>
<li><strong>Promise 实现了回调函数的延时绑定</strong></li>
</ul>
<p>产生嵌套回调的主要原因是在发起任务请求时会带上回调函数，所以当前任务结束后下个任务只能在回调函数中处理。而 Promise 是在创建好 Promise 对象后再在 Promise 对象上调用 <code>.then</code> 方法来设置回调函数：</p>
<ul>
<li><strong>Promise 将回调函数的返回值穿透到最外层</strong></li>
</ul>
<p>通过将回调函数中创建的 Promise 对象返回到最外层可以摆脱嵌套循环。</p>
<ul>
<li><strong>Promise 错误"冒泡"解决异常的重复处理</strong></li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">executor</span>(<span class="params">resolve, reject</span>) &#123;</span><br><span class="line">    <span class="keyword">let</span> rand = <span class="title class_">Math</span>.<span class="title function_">random</span>();</span><br><span class="line">    <span class="keyword">if</span> (rand &gt; <span class="number">0.5</span>) &#123; <span class="title function_">resolve</span>() &#125;</span><br><span class="line">    <span class="keyword">else</span> &#123; <span class="title function_">reject</span>() &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> p0 = <span class="keyword">new</span> <span class="title class_">Promise</span>(executor)</span><br><span class="line"><span class="keyword">var</span> p1 = p0.<span class="title function_">then</span>(<span class="function">(<span class="params">value</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Promise</span>(executor)</span><br><span class="line">&#125;)</span><br><span class="line"><span class="keyword">var</span> p2 = p1.<span class="title function_">then</span>(<span class="function">(<span class="params">value</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Promise</span>(executor)</span><br><span class="line">&#125;)</span><br><span class="line">p2.<span class="title function_">catch</span>(<span class="function">(<span class="params">error</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;error&quot;</span>)</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>

<p>Promise 对象的错误具有"冒泡"性质，会一直向后传递直到被 onReject 函数处理或 catch 语句捕获为止。三个 Promise 对象 p0～p2 无论哪个对象中抛出异常，都可以在最后一个对象 p2.catch 中捕获异常，合并了错误处理。</p>
<ul>
<li>Promise与微任务</li>
</ul>
<p>使用定时器的效率并不是太高，所以 Promise 将这个定时器的实现改造成微任务，这样既可以让 onResolve 延迟调用，又提升了代码的执行效率，这就是 Promise 中使用微任务的原由。</p>
<ul>
<li><strong>async / wait</strong></li>
</ul>
<p>Promise 的编程模型虽然解决了回调地狱问题，但在语义方面依然存在缺陷，代码中充斥着大量的 then 函数。使用 es7 的 async/await 可以实现用同步代码风格来编写异步代码，async/await 的基础技术使用生成器(协程)和 Promise(微任务) 来实现。</p>
<blockquote>
<h3 id="生成器-协程"><a href="#生成器-协程" class="headerlink" title="生成器 & 协程"></a>生成器 & 协程</h3><p>生成器函数是一个支持暂停和恢复执行的带星号函数。在生成器函数内部执行一段代码，遇到 yield 关键字 JavaScript 引擎会暂停该函数的执行并将关键字后的内容返回给外部，外部函数可通过 next 方法恢复函数的执行。</p>
<p>生成器就是 JavaScript 中协程的一种实现方式。协程是一种比线程更轻量级的存在，一个线程可以拥有多个协程，但线程上同时只能执行一个协程，可以把协程看成是跑在线程上的任务，比如当前执行 A 协程同时要启动 B 协程，那么 A 协程需要将主线程的控制权交给 B 协程，体现为 A 协程暂停执行，B 协程恢复执行。协程不被操作系统内核管理，完全由程序控制(用户态执行)，因此性能得到很大提升，不会像线程切换那样消耗资源。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">&gt;<span class="keyword">function</span>* <span class="title function_">genDemo</span>(<span class="params"></span>) &#123;</span><br><span class="line">   <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;开始执行第一段&quot;</span>)</span><br><span class="line">   <span class="keyword">yield</span> <span class="string">&#x27;generator 1&#x27;</span></span><br><span class="line">   <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;开始执行第二段&quot;</span>)</span><br><span class="line">   <span class="keyword">yield</span> <span class="string">&#x27;generator 2&#x27;</span></span><br><span class="line">   <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;执行结束&quot;</span>)</span><br><span class="line">   <span class="keyword">return</span> <span class="string">&#x27;generator 3&#x27;</span></span><br><span class="line">&gt;&#125;</span><br><span class="line">&gt;<span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;main 0&#x27;</span>)</span><br><span class="line">&gt;<span class="keyword">let</span> gen = <span class="title function_">genDemo</span>()</span><br><span class="line">&gt;<span class="variable language_">console</span>.<span class="title function_">log</span>(gen.<span class="title function_">next</span>().<span class="property">value</span>)</span><br><span class="line">&gt;<span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;main 1&#x27;</span>)</span><br><span class="line">&gt;<span class="variable language_">console</span>.<span class="title function_">log</span>(gen.<span class="title function_">next</span>().<span class="property">value</span>)</span><br><span class="line">&gt;<span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;main 2&#x27;</span>)</span><br><span class="line">&gt;<span class="variable language_">console</span>.<span class="title function_">log</span>(gen.<span class="title function_">next</span>().<span class="property">value</span>)</span><br><span class="line">&gt;<span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;main 3&#x27;</span>)</span><br></pre></td></tr></table></figure>
</blockquote>
<blockquote>
<p><img src="/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220221154511828.png" alt="image-20220221154511828"></p>
</blockquote>
<blockquote>
<p>根据 MDN 定义，<code>async</code> 是一个通过 <strong>异步执行</strong> 并 <strong>隐式返回 Promise</strong> 的函数。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">async</span> <span class="keyword">function</span> <span class="title function_">foo</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">2</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="title function_">foo</span>()) <span class="comment">// Promise &#123;&lt;resolved&gt;: 2&#125;</span></span><br></pre></td></tr></table></figure>

<p><code>await</code> 则会默认创建 Promise 对象。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">async</span> <span class="keyword">function</span> <span class="title function_">foo</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">1</span>) <span class="comment">// step2</span></span><br><span class="line">    <span class="keyword">let</span> a = <span class="keyword">await</span> <span class="number">100</span></span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(a) <span class="comment">// step4</span></span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">2</span>) <span class="comment">// step5</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">0</span>) <span class="comment">// step1</span></span><br><span class="line"><span class="title function_">foo</span>()</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">3</span>) <span class="comment">// step3</span></span><br></pre></td></tr></table></figure>
</blockquote>
<p><img src="/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220221155547371.png" alt="image-20220221155547371"></p>
<blockquote>
<p>从协程的视角观察代码的整体执行流程，执行 <code>await 100</code> 语句时会默认创建一个 Promise 对象，然后 JavaScript 引擎会暂停当前 foo 协程，将主线程的控制权转交给父协程，同时将创建的 promise *对象返回给父协程。父协程需要调用 <code>promise*.then</code>来监控 promise 状态的改变。接下来继续执行父协程打印 3。随后父协程执行结束前会进入微任务检查点，然后执行微任务队列，微任务队列中的<code>resolve(100)</code> 任务被执行，最后执行 foo 中剩下流程。</p>
</blockquote>
<p><code>async / await</code> 其实是 Promise 和生成器的应用，也可以说是微任务和协程应用，使用 <code>async / await</code> 能够彻底告别执行器和生成器，实现更直观简洁的代码。</p>
<p><strong>Promise.all()</strong></p>
<p>Promise.all方法接受一个数组作为参数，但每个参数必须是一个Promise实例。Promise的all方法提供了并行执行异步操作的能力，并且在所有异步操作都执行完毕后才执行回调，只要其中一个异步操作返回的状态为rejected那么Promise.all()返回的Promise即为rejected状态，此时第一个被reject的实例的返回值，会传递给Promise.all的回调函数</p>
<p><img src="/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220221222707415.png" alt="image-20220221222707415"></p>
<p><strong>Promise.race()</strong></p>
<p>Promise的race方法和all方法类似，都提供了并行执行异步操作的能力，但是all方法的效果实际上是「谁跑的慢，以谁为准执行回调」，而race方法则是「谁跑的快，以谁为准执行回调」，以下就是race的执行过程：</p>
<p><img src="/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220221222720236.png" alt="image-20220221222720236"></p>
<h2 id="29-页面渲染性能优化"><a href="#29-页面渲染性能优化" class="headerlink" title="29.页面渲染性能优化"></a>29.页面渲染性能优化</h2><p>见23.4线程视角</p>
<p><a target="_blank" rel="noopener" href="https://cloud.tencent.com/developer/article/1623162">浏览器原理学习笔记05—浏览器中的页面渲染 - 云+社区 - 腾讯云 (tencent.com)</a></p>
<p><a target="_blank" rel="noopener" href="https://cloud.tencent.com/developer/article/1441958">页面性能优化的五种办法 - 云+社区 - 腾讯云 (tencent.com)</a></p>
<p><a target="_blank" rel="noopener" href="https://vue3js.cn/interview/vue/first_page_time.html#%E4%BA%8C%E3%80%81%E5%8A%A0%E8%BD%BD%E6%85%A2%E7%9A%84%E5%8E%9F%E5%9B%A0">面试官：SPA首屏加载速度慢的怎么解决？ | web前端面试 - 面试官系列 (vue3js.cn)</a></p>
<h3 id="加载阶段"><a href="#加载阶段" class="headerlink" title="加载阶段"></a>加载阶段</h3><p>指从请求发出到渲染出完整页面的过程，影响主要因素：网络、 JavaScript 脚本。</p>
<p><strong>（首屏加载）</strong></p>
<p>加载速度慢的因素可能如下：</p>
<table>
<thead>
<tr>
<th>因素</th>
<th>处理</th>
</tr>
</thead>
<tbody><tr>
<td>网络延时问题</td>
<td>CDN内容分发网络加速，减少rtt</td>
</tr>
<tr>
<td>资源文件体积是否过大</td>
<td>减小入口文件体积–&gt;路由懒加载import(‘…’)</td>
</tr>
<tr>
<td>资源是否重复发送请求去加载了</td>
<td>采用前端缓存，组件库重复打包（设置webpack）</td>
</tr>
<tr>
<td>加载脚本的时候，渲染内容堵塞了</td>
<td>defer,async(加载完立即执行),预加载</td>
</tr>
</tbody></table>
<p><img src="/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/4fafe900-3acc-11eb-85f6-6fac77c0c9b3.png" alt="image.png"></p>
<p><img src="/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220221161915981.png" alt="image-20220221161915981"></p>
<p>图片、音频、视频等文件不会阻塞页面的首次渲染，而 JavaScript、首次请求的 HTML 文件、CSS 文件会阻塞首次渲染(构建 DOM 需要 HTML 和  JavaScript 文件，构造渲染树需要 CSS 文件)，称为关键资源，三个优化关键资源的方式：</p>
<ul>
<li><strong>减少关键资源个数</strong></li>
<li>将 JavaScript 和 CSS 改成内联形式；</li>
<li>若  JavaScript 代码没有 DOM 或 CSSOM 操作，改成 async 或 defer 属性，变成非关键资源；</li>
<li>若 CSS 不必在构建页面之前加载，添加媒体取消阻止显现的标志，变成非关键资源</li>
</ul>
<blockquote>
<p><strong>触发异步样式下载：</strong> 为 media 属性设置一个不可用的值，如"none"，当媒体查询结果值计算为 false，浏览器仍会下载样式表，但不会在渲染页面之前等待样式表的资源可用</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&lt;link rel=<span class="string">&quot;stylesheet&quot;</span> href=<span class="string">&quot;test.css&quot;</span> media=<span class="string">&quot;none&quot;</span> onload=<span class="string">&quot;if(media!=&#x27;all&#x27;)media=&#x27;all&#x27;&quot;</span>&gt;</span><br></pre></td></tr></table></figure>

<ul>
<li><p><strong>减少关键资源大小</strong></p>
<p>压缩、移除代码注释、变成非关键资源等</p>
</li>
<li><p><strong>减少关键资源 RTT (Round Trip Time) 次数</strong></p>
<p>使用 CDN; 减少关键资源个数和大小搭配。</p>
</li>
</ul>
<p>另一篇文章的总结：</p>
<ul>
<li><p>减小入口文件积--->路由懒加载</p>
</li>
<li><p>静态资源本地缓存</p>
<blockquote>
<p>--->service worker离线缓存，http缓存，设置响应头（见前端缓存）</p>
</blockquote>
</li>
<li><p>UI框架按需加载</p>
<blockquote>
<p>按需引入element-ui等组件</p>
</blockquote>
</li>
<li><p>图片资源的压缩</p>
</li>
<li><p>组件重复打包</p>
<blockquote>
<p>假设<code>A.js</code>文件是一个常用的库，现在有多个路由使用了<code>A.js</code>文件，这就造成了重复下载</p>
<p>解决方案：在<code>webpack</code>的<code>config</code>文件中，修改<code>CommonsChunkPlugin</code>的配置</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">minChunks</span>: <span class="number">3</span></span><br></pre></td></tr></table></figure>

<p><code>minChunks</code>为3表示会把使用3次及以上的包抽离出来，放进公共依赖文件，避免了重复加载组件</p>
</blockquote>
</li>
<li><p>使用SSR(服务端渲染)</p>
</li>
</ul>
<h3 id="2-交互阶段"><a href="#2-交互阶段" class="headerlink" title="2 交互阶段"></a>2 交互阶段</h3><p>指从页面加载完成到用户交互的过程，即渲染进程渲染帧的速度，影响主要因素：JavaScript 脚本。</p>
<p>加载阶段不同的是，交互阶段没有了加载关键资源和构建 DOM、CSSOM 流程，大部分是由 JavaScript 通过修改 DOM 或者 CSSOM 触发交互动画的，另外一部分帧是由 CSS 触发的。如果在计算样式阶段发现有布局信息修改，会触发<strong>重排</strong>操作；若不涉及布局相关的调整，只是修改了颜色一类信息，就可以跳过布局阶段直接触发<strong>重绘</strong>操作；若通过 CSS 触发一些变形、渐变、动画等特效，只会触发合成线程上的<strong>合成</strong>操作，效率最高。优化单帧生成速度的方法：</p>
<ul>
<li><p><strong>减少 JavaScript 脚本执行时间</strong></p>
<p>避免单任务霸占主线程太久，将大任务分解为多个小任务，也可以使用 Web Workers 在主线程外的一个线程中执行和 DOM 操作无关且耗时的任务(Web Workers 中没有 DOM、CSSOM 环境)</p>
</li>
<li><p><strong>避免强制同步布局</strong></p>
<p>通过 DOM 接口执行元素添加或删除等操作后，为避免当前任务占用主线程太长时间，一般重新计算样式和布局操作是在另外的任务中异步完成的。</p>
</li>
<li><p><strong>避免布局抖动</strong></p>
<p>布局抖动是指在一次 JavaScript 执行过程中，多次执行强制布局和抖动操作，应该尽量避免在修改 DOM 结构时再查询一些相关值。</p>
</li>
<li><p><strong>合理利用 CSS 合成动画</strong></p>
<p>合成动画在合成线程上执行，不影响主线程，还可以使用 will-change 标记生成单独图层。</p>
</li>
<li><p><strong>避免频繁垃圾回收</strong></p>
<p>垃圾回收占用主线程，可以尽可能<strong>优化储存结构，避免产生小颗粒对象</strong>，避免产生临时垃圾数据。</p>
</li>
</ul>
<h3 id="虚拟DOM"><a href="#虚拟DOM" class="headerlink" title="虚拟DOM"></a>虚拟DOM</h3><h4 id="DOM-的缺陷"><a href="#DOM-的缺陷" class="headerlink" title="DOM 的缺陷"></a>DOM 的缺陷</h4><p>通过 JavaScript 操纵 DOM 会影响整个渲染流水线，触发样式计算、布局、绘制、栅格化、合成等任务，牵一发而动全身，对 DOM 的不当操作还可能引发强制同步布局和布局抖动问题，尤其对复杂页面会造成性能问题。而通过 JavaScript 实现的更轻量的虚拟 DOM 可以解决上述问题。</p>
<h4 id="V-DOM执行流程"><a href="#V-DOM执行流程" class="headerlink" title="V DOM执行流程"></a>V DOM执行流程</h4><ul>
<li><strong>创建阶段</strong>：以 React 为例，首先依据 JSX 和基础数据创建虚拟 DOM，然后由虚拟 DOM 树创建出真实 DOM 树，再触发渲染流水线输出页面。</li>
<li><strong>更新阶段</strong>：数据发生改变时会根据新数据创建一个新的虚拟 DOM 树，然后 React 比较两个树，找出变化的地方，并将变化的地方一次性更新到真实 DOM 树上，最后渲染引擎更新渲染流水线，并生成新的页面。</li>
</ul>
<h2 id="30-Vue源码"><a href="#30-Vue源码" class="headerlink" title="30. Vue源码"></a>30. Vue源码</h2><h3 id="前置知识储备："><a href="#前置知识储备：" class="headerlink" title="前置知识储备："></a>前置知识储备：</h3><p><a target="_blank" rel="noopener" href="https://cloud.tencent.com/developer/article/1441327">【前端词典】学习 Vue 源码的必要知识储备 - 云+社区 - 腾讯云 (tencent.com)</a></p>
<ol>
<li><p>Flow 基本语法：静态类型检查工具</p>
</li>
<li><p>发布/订阅模式：</p>
<blockquote>
<p>其实 Vue 的双向绑定机制采用<strong>数据劫持结合发布/订阅模式</strong>实现的: 通过 <code>Object.defineProperty()</code> 来劫持各个属性的 <code>setter，getter</code>，在数据变动时发布消息给订阅者，触发相应的监听回调。</p>
<p><img src="/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220221224205075.png" alt="image-20220221224205075"></p>
</blockquote>
</li>
<li><p>Object.defineProperty</p>
<blockquote>
<p>数据双向绑定基础：Object.defineProperty()</p>
<h4 id="一、数据属性"><a href="#一、数据属性" class="headerlink" title="一、数据属性"></a>一、数据属性</h4><p>四个基本属性：</p>
<p><img src="/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220221225756729.png" alt="image-20220221225756729"></p>
<p><strong>要修改上述 4 个默认的数据属性</strong>，使用 ECMAScript 的 Object.defineProperty() 方法。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> person = &#123;  <span class="attr">name</span>: <span class="string">&#x27;&#x27;</span>,&#125;;<span class="comment">// 不能修改属性的值</span></span><br><span class="line"><span class="title class_">Object</span>.<span class="title function_">defineProperty</span>(person, <span class="string">&quot;name&quot;</span>,&#123;<span class="attr">writable</span>: <span class="literal">false</span>,<span class="attr">value</span>: <span class="string">&quot;小生方勤&quot;</span>&#125;)</span><br></pre></td></tr></table></figure>

<h4 id="二、访问器属性"><a href="#二、访问器属性" class="headerlink" title="二、访问器属性"></a>二、访问器属性</h4><p>访问器属性不包含数据值，他们包含一对 <code>getter</code> 和 <code>setter</code> 函数（非必须）。在读写访问器属性的值的时候，会调用相应的 <code>getter</code> 和 <code>setter</code> 函数，而 vue 就是在 <code>getter</code> 和 <code>setter</code> 函数中增加了我们需要的操作。</p>
<p>【value 或 writable】一定不能和【get 或 set】共存</p>
<p><img src="/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220221230323016.png"></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="title class_">Object</span>.<span class="title function_">defineProperty</span>(person, <span class="string">&quot;name&quot;</span>, &#123;  </span><br><span class="line"> <span class="attr">set</span>: <span class="keyword">function</span>(<span class="params">value</span>)&#123;  </span><br><span class="line">     <span class="variable language_">this</span>.<span class="property">_name</span> = <span class="string">&quot;来自 setter : &quot;</span> + value;      </span><br><span class="line"> &#125;,    </span><br><span class="line"> <span class="attr">get</span>: <span class="keyword">function</span>(<span class="params"></span>)&#123;        </span><br><span class="line">     <span class="keyword">return</span> <span class="string">&quot;来自 getter : &quot;</span> + <span class="variable language_">this</span>.<span class="property">_name</span>;    </span><br><span class="line"> &#125;&#125;);</span><br></pre></td></tr></table></figure>

<p><strong>Object.create(null)真正的创建一个纯净的对象</strong></p>
<h3 id="proxy"><a href="#proxy" class="headerlink" title="proxy"></a>proxy</h3><p><code>Proxy</code>是 <code>Object.defineProperty</code>的加强版。</p>
<p>Proxy在目标对象之前架设一层“拦截”，外界对该对象的访问，都必须先通过这层拦截，可以对外界的访问进行过滤和改写。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">var proxy = new Proxy(target, handler);</span><br></pre></td></tr></table></figure>


</blockquote>
</li>
<li><p>ES6+ 语法</p>
<blockquote>
<p> <code>exportdefault</code> 和 <code>export</code> 的区别</p>
<ol>
<li>在一个文件或模块中 <code>export</code> 可以有多个，但 <code>exportdefault</code> 仅有一个</li>
<li>通过 <code>export</code> 方式导出，在导入时要加 { }，而 <code>exportdefault</code> 则不需要</li>
</ol>
<p><strong>class 继承</strong></p>
<p>Class 可以通过 <code>extends</code> 关键字实现继承，这比 ES5 的通过修改原型链实现继承，要清晰和方便很多。</p>
<ol>
<li>子类必须在 <code>constructor</code> 方法中调用 <code>super</code> 方法，否则新建实例时会报错。这是因为子类没有自己的 <code>this</code> 对象，而是继承父类的 <code>this</code> 对象，然后对其进行加工。</li>
<li>只有调用 <code>super</code> 之后，才可以使用 <code>this</code> 关键字，否则会报错。这是因为子类实例的构建，是基于对父类实例加工，只有 <code>super</code> 方法才能返回父类实例。</li>
<li>ES5 的继承，实质是先创造子类的实例对象 <code>this</code>，然后再将父类的方法添加到 <code>this</code> 上面（ <code>Parent.apply(this)</code>）。 ES6 的继承机制完全不同，实质是先创造父类的实例对象 <code>this</code> （所以必须先调用 <code>super()</code> 方法），然后再用子类的构造函数修改 <code>this</code>。</li>
</ol>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">`super` 虽然代表了父类 `A` 的构造函数，但是返回的是子类 `B` 的实例，即` super` 内部的 `this ` 指的是 `B`，因此 `super()` 在这里相当于 A.prototype.constructor.call(this)</span><br></pre></td></tr></table></figure>
</blockquote>
</li>
<li><p>原型链、闭包</p>
</li>
<li><p>函数柯里化</p>
</li>
<li><p>event loop</p>
</li>
</ol>
<h2 id="31-ECMAScript、JavaScript、nodejs的区别和联系"><a href="#31-ECMAScript、JavaScript、nodejs的区别和联系" class="headerlink" title="31. ECMAScript、JavaScript、nodejs的区别和联系"></a>31. ECMAScript、JavaScript、nodejs的区别和联系</h2><p>ECMAScript: 定义语法，js必须遵守，是对js的一种规范，仅仅是一种规范而已</p>
<p><strong>JavaScript</strong></p>
<blockquote>
<p>使用ECMAScript语法规范，外加Web API（使用W3C标准），缺一不可<br>Web API 包含 DOM 操作，BOM 操作，事件绑定，Ajax 等<br>两者结合（es语法规范+web api），即可完成浏览器端的任何操作<br>基于js引擎，不同的浏览器有不同的js引擎</p>
</blockquote>
<p><strong>Node.js</strong></p>
<blockquote>
<p>使用ECMAScript语法规范，外加nodejs API，缺一不可<br>nodejs API包含OS（操作系统模块）、file（文件系统模块）、net（网络系统模块）、database（数据库模块）<br>两者结合（es语法规范+nodejs api），即可完成服务端的任何操作<br>基于Google的V8引擎，执行速度快，性能很好</p>
</blockquote>
<h3 id="联系"><a href="#联系" class="headerlink" title="联系"></a><strong>联系</strong></h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">nodejs = ECMAScript + nodejs API		//模块化使用commonjs规范</span><br><span class="line">JavaScript = ECMAScript + Web API = ECMAScript + DOM + BOM  //模块化目前是es6</span><br></pre></td></tr></table></figure>

<h3 id="区别"><a href="#区别" class="headerlink" title="区别"></a>区别</h3><p><strong>全局对象</strong><br>JavaScript全局对象为window<br>nodejs全局对象为global<br><strong>全局属性</strong><br>JavaScript中定义的全局变量可以通过window访问<br>nodejs中定义的全局属性不能通过global访问（因为在nodejs中定义的变量是模块中的对象，而并不是global全局变量）<br><strong>模块化</strong><br>JavaScript的模块化经历如下过程：CommonJS规范–>AMD规范–>CMD规范–><strong>ES6模块化</strong><br>nodejs采用的是<strong>CommonJs规范</strong>，使用require module.exports的写法，<strong>不支持ES6中import export的写法</strong></p>
<ul>
<li>commonjs模块化规范输出的是一个值的拷贝，ES6 模块输出的是值的引用。</li>
<li>commonjs模块化规范是运行时加载，ES6 模块是编译时输出接口。</li>
</ul>
<h2 id="32-前端缓存"><a href="#32-前端缓存" class="headerlink" title="32. 前端缓存"></a>32. 前端缓存</h2><p><a target="_blank" rel="noopener" href="https://cloud.tencent.com/developer/article/1441969">前端缓存最佳实践 - 云+社区 - 腾讯云 (tencent.com)</a></p>
<h3 id="HTTP缓存"><a href="#HTTP缓存" class="headerlink" title="HTTP缓存"></a>HTTP缓存</h3><p><strong><code>HTTP</code>缓存策略只是为了解决客户端和服务端信息不对称的问题而存在的，客户端为了加快速度会缓存部分资源，但是下次请求时，客户端不知道这个资源有没有更新，服务端也不知道客户端缓存的是哪个版本，不知道该不该再返回资源，其实就是一个信息同步问题，<code>HTTP</code>缓存策略就是来解决这个问题的</strong></p>
<p><a target="_blank" rel="noopener" href="http://www.dennisgo.cn/Articles/Network/HttpCache.html">轻松理解HTTP缓存策略 · 前端进阶 (dennisgo.cn)</a></p>
<p><strong>协商缓存</strong>：为了知道有没有更新，必须跟服务端沟通过才知道的，我们称之为<strong>协商缓存</strong>。</p>
<p><strong>强制缓存</strong>：不用跟服务器协商直接用本地副本的叫做<strong>强制缓存</strong>。一般协商好了更新时间，到点自己会取</p>
<p><strong>优先级：强制大于协商</strong></p>
<h3 id="协商缓存"><a href="#协商缓存" class="headerlink" title="协商缓存"></a>协商缓存</h3><h4 id="1-ETag-和-If-None-Match"><a href="#1-ETag-和-If-None-Match" class="headerlink" title="1. ETag 和 If-None-Match"></a>1. ETag 和 If-None-Match</h4><p>server发给client一个版本号etags</p>
<p><strong>If-None-Match</strong></p>
<p>client拿现在的版本号放在请求头的If-None-Match，去问server，server对比,是最新版就304不发数据，不是最新版就200发数据</p>
<p><strong>If-Match</strong></p>
<p>改数据的时候，如果匹配才能提交，一个client在修改时，另一个client也修改了，这时候不匹配，返回412，匹配就返回200</p>
<h4 id="2-Last-Modified-If-Modified-Since"><a href="#2-Last-Modified-If-Modified-Since" class="headerlink" title="2. Last-Modified & If-Modified-Since"></a>2. Last-Modified & If-Modified-Since</h4><p>跟前面类似</p>
<p><code>ETag</code>放的是一个版本号或者<code>hash</code>值，<code>Last-Modified</code>放的是资源的最后修改时间</p>
<h4 id="ETag和Last-Modified优先级"><a href="#ETag和Last-Modified优先级" class="headerlink" title="ETag和Last-Modified优先级"></a>ETag和Last-Modified优先级</h4><p><code>ETag</code>和<code>Last-Modified</code>都是协商缓存，都需要服务器进行计算和比较，那如果这两个都存在，用哪个呢？<strong>答案是<code>ETag</code>，<code>ETag</code>的优先级比<code>Last-Modified</code>高</strong>。</p>
<p>LastModified精确度秒，精确度低，etag每次都是新的，但是有额外性能消耗来计算hash。</p>
<h3 id="强制缓存"><a href="#强制缓存" class="headerlink" title="强制缓存"></a>强制缓存</h3><h4 id="1-Expires"><a href="#1-Expires" class="headerlink" title="1.Expires"></a>1.Expires</h4><p><code>Expires</code>比较简单，就是服务器<code>response</code>的<code>header</code>带上这个字段：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="title class_">Expires</span>: <span class="title class_">Wed</span>, <span class="number">21</span> <span class="title class_">Oct</span> <span class="number">2000</span> <span class="number">07</span>:<span class="number">28</span>:<span class="number">00</span> <span class="variable constant_">GMT</span></span><br></pre></td></tr></table></figure>

<p>在这个时间内不用发请求，这个时间才更新</p>
<h4 id="2-Cache-Control"><a href="#2-Cache-Control" class="headerlink" title="2. Cache-Control"></a>2. Cache-Control</h4><h4 id="Cache-Control-Expries"><a href="#Cache-Control-Expries" class="headerlink" title="Cache-Control>Expries"></a>Cache-Control>Expries</h4><p><code>Cache-Control</code>相对比较复杂，可设置属性也比较多，<code>max-age</code>只是其中一个属性，长这样：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="title class_">Cache</span>-<span class="title class_">Control</span>: max-age=<span class="number">20000</span></span><br></pre></td></tr></table></figure>

<p>这表示当<strong>前资源在<code>20000秒</code>内都不用再请求了</strong>，直接使用缓存。</p>
<p>上面提到的<code>immutable</code>也是<code>Cache-Control</code>的一个属性，但是是个实验性质的，各个浏览器兼容并不好。设置了<code>Cache-control: immutable</code>表示这辈子都用缓存了，再请求是不可能的了。</p>
<p>其他常用属性还有：</p>
<p><code>no-cache</code>：使用缓存前，强制要求把请求提交给服务器进行验证(协商缓存验证)。</p>
<p><code>no-store</code>：不存储有关客户端请求或服务器响应的任何内容，即<strong>不使用任何缓存</strong>。</p>
<h2 id="33-生成器和协程async-await"><a href="#33-生成器和协程async-await" class="headerlink" title="33. 生成器和协程async/await"></a>33. 生成器和协程async/await</h2><p><a target="_blank" rel="noopener" href="http://www.dennisgo.cn/Articles/JavaScript/Generator.html">Generator和async/await还有co源码 · 前端进阶 (dennisgo.cn)</a></p>
<p>生成器的例子：解决异步调用</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> request = <span class="built_in">require</span>(<span class="string">&quot;request&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span>* <span class="title function_">requestGen</span>(<span class="params"></span>) &#123;</span><br><span class="line">  <span class="keyword">function</span> <span class="title function_">sendRequest</span>(<span class="params">url</span>) &#123;</span><br><span class="line">    <span class="title function_">request</span>(url, <span class="keyword">function</span> (<span class="params">error, response</span>) &#123;</span><br><span class="line">      <span class="keyword">if</span> (!error &amp;&amp; response.<span class="property">statusCode</span> == <span class="number">200</span>) &#123;</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(response.<span class="property">body</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 注意这里，引用了外部的迭代器itor</span></span><br><span class="line">        itor.<span class="title function_">next</span>(response.<span class="property">body</span>);<span class="comment">//itor.next()括号里的值作为返回值传给上次结果。</span></span><br><span class="line">      &#125;</span><br><span class="line">    &#125;)</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">const</span> url = <span class="string">&#x27;https://www.baidu.com&#x27;</span>;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 使用yield发起三个请求，每个请求成功后再继续调next</span></span><br><span class="line">  <span class="keyword">const</span> r1 = <span class="keyword">yield</span> <span class="title function_">sendRequest</span>(url);</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;r1&#x27;</span>, r1);</span><br><span class="line">  <span class="keyword">const</span> r2 = <span class="keyword">yield</span> <span class="title function_">sendRequest</span>(url);</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;r2&#x27;</span>, r2);</span><br><span class="line">  <span class="keyword">const</span> r3 = <span class="keyword">yield</span> <span class="title function_">sendRequest</span>(url);</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;r3&#x27;</span>, r3);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> itor = <span class="title function_">requestGen</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 手动调第一个next</span></span><br><span class="line">itor.<span class="title function_">next</span>();</span><br></pre></td></tr></table></figure>

<p> <strong>co模块</strong></p>
<p>co模块是一个很受欢迎的模块，可以自动执行Generator，他的yield后面支持thunk和Promise</p>
<p>co可以<strong>使用Promise</strong>，我们代码写起来其实更简单，直接用fetch就行，不用包装Thunk。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> fetch = <span class="built_in">require</span>(<span class="string">&#x27;node-fetch&#x27;</span>);</span><br><span class="line"><span class="keyword">const</span> co = <span class="built_in">require</span>(<span class="string">&#x27;co&#x27;</span>);</span><br><span class="line"><span class="title function_">co</span>(<span class="keyword">function</span>* () &#123;</span><br><span class="line">  <span class="comment">// 直接用fetch，简单多了，fetch返回的就是Promise</span></span><br><span class="line">  <span class="keyword">const</span> r1 = <span class="keyword">yield</span> <span class="title function_">fetch</span>(<span class="string">&#x27;https://www.baidu.com&#x27;</span>);</span><br><span class="line">  <span class="keyword">const</span> r2 = <span class="keyword">yield</span> <span class="title function_">fetch</span>(<span class="string">&#x27;https://www.baidu.com&#x27;</span>);</span><br><span class="line">  <span class="keyword">const</span> r3 = <span class="keyword">yield</span> <span class="title function_">fetch</span>(<span class="string">&#x27;https://www.baidu.com&#x27;</span>);</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> &#123;</span><br><span class="line">    r1,</span><br><span class="line">    r2,</span><br><span class="line">    r3,</span><br><span class="line">  &#125;</span><br><span class="line">&#125;).<span class="title function_">then</span>(<span class="function">(<span class="params">res</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="comment">// 这里同样可以拿到&#123;r1, r2, r3&#125;</span></span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(res);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">fetch存在问题</span><br><span class="line">fetch是一个低层次的<span class="variable constant_">API</span>，你可以把它考虑成原生的<span class="variable constant_">XHR</span>，所以使用起来并不是那么舒服，需要进行封装。</span><br><span class="line"></span><br><span class="line">fetch只对网络请求报错，对<span class="number">400</span>，<span class="number">500</span>都当做成功的请求，服务器返回 <span class="number">400</span>，<span class="number">500</span> 错误码时并不会 reject，只有网络错误这些导致请求不能完成时，fetch 才会被 reject。</span><br><span class="line">fetch默认不会带cookie，需要添加配置项： <span class="title function_">fetch</span>(url, &#123;<span class="attr">credentials</span>: <span class="string">&#x27;include&#x27;</span>&#125;)</span><br><span class="line">fetch不支持abort，不支持超时控制，使用<span class="built_in">setTimeout</span>及<span class="title class_">Promise</span>.<span class="property">reject</span>的实现的超时控制并不能阻止请求过程继续在后台运行，造成了流量的浪费</span><br><span class="line">fetch没有办法原生监测请求的进度，而<span class="variable constant_">XHR</span>可以</span><br></pre></td></tr></table></figure>

<p><strong>协程</strong></p>
<blockquote>
<p>其实Generator就是实现了协程，协程是一个比线程还小的概念。一个进程可以有多个线程，一个线程可以有多个协程，但是一个线程同时只能有一个协程在运行。这个意思就是说如果当前协程可以执行，比如同步代码，那就执行他，如果当前协程暂时不能继续执行，比如他是一个异步读文件的操作，那就将它挂起，然后去执行其他协程，等这个协程结果回来了，可以继续了再来执行他。<code>yield</code>其实就相当于将当前任务挂起了，下次调用再从这里开始。协程这个概念其实很多年前就已经被提出来了，其他很多语言也有自己的实现。Generator相当于JS实现的协程。</p>
</blockquote>
<p><strong>async/await</strong>，</p>
<p>用法:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> fetch = <span class="built_in">require</span>(<span class="string">&#x27;node-fetch&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">function</span> <span class="title function_">sendRequest</span> () &#123;</span><br><span class="line">  <span class="keyword">const</span> r1 = <span class="keyword">await</span> <span class="title function_">fetch</span>(<span class="string">&#x27;https://www.baidu.com&#x27;</span>);</span><br><span class="line">  <span class="keyword">const</span> r2 = <span class="keyword">await</span> <span class="title function_">fetch</span>(<span class="string">&#x27;https://www.baidu.com&#x27;</span>);</span><br><span class="line">  <span class="keyword">const</span> r3 = <span class="keyword">await</span> <span class="title function_">fetch</span>(<span class="string">&#x27;https://www.baidu.com&#x27;</span>);</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> &#123;</span><br><span class="line">    r1,</span><br><span class="line">    r2,</span><br><span class="line">    r3,</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 注意async返回的也是一个promise</span></span><br><span class="line"><span class="title function_">sendRequest</span>().<span class="title function_">then</span>(<span class="function">(<span class="params">res</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;res&#x27;</span>, res);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<p>咋一看这个跟前面promise版的co是不是很像，返回值都是一个promise，只是Generator换成了一个<code>async</code>函数，函数里面的<code>yield</code>换成了<code>await</code>，而且外层不需要co来包裹也可以自动执行了。其实async函数就是Generator加自动执行器的语法糖，可以理解为从语言层面支持了Generator的自动执行。上面这段代码跟co版的promise其实就是等价的。</p>
<h2 id="34-数据类型转换与检测"><a href="#34-数据类型转换与检测" class="headerlink" title="34. 数据类型转换与检测"></a>34. 数据类型转换与检测</h2><p><a target="_blank" rel="noopener" href="http://www.dennisgo.cn/Articles/JavaScript/Types.html">数据类型转换和检测 · 前端进阶 (dennisgo.cn)</a></p>
<p>下面的值在进行bool转换时会转换为<code>false</code>，除此以外都是<code>true</code>:</p>
<ol>
<li>0</li>
<li>NaN</li>
<li>''(空字符串)</li>
<li>null</li>
<li>undefined</li>
</ol>
<img src="%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220222203751219.png" alt="image-20220222203751219" style="zoom: 50%;" />

<p><code>null</code>在这里我们算在原始类型里面，但是你用<code>typeof</code>的时候会发现他是<code>object</code>，原因是就算他是一个对象，那他应该在栈中存一个引用地址，但是他是一个空对象，所以这个地址为空，也就是不对应堆中的任意一个数据，他在堆中没有数据，只存在于栈中，所以这里算为了原始类型。</p>
<p>引用类型其实主要就是<code>Object</code>，<code>Array</code>和<code>Function</code>这些其实也都是<code>Object</code>派生出来的</p>
<p><strong>类型检测</strong></p>
<p>JS其实没有一种完美的方法来检测所有的类型，具体的检测方法需要我们根据实际情况来进行选择和取舍。</p>
<p><img src="/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220222204048854.png" alt="image-20220222204048854"></p>
<h2 id="35-Vue响应式原理"><a href="#35-Vue响应式原理" class="headerlink" title="35. Vue响应式原理"></a>35. Vue响应式原理</h2><p>[从vue源码看Vue.set()和this.$set() - 你假装没察觉 - 博客园 (cnblogs.com)</p>
<p><a target="_blank" rel="noopener" href="https://www.cnblogs.com/heavenYJJ/p/9559439.html"></a></p>
<p><a target="_blank" rel="noopener" href="https://zhuanlan.zhihu.com/p/138710460">如何理解vue数据双向绑定原理 - 知乎 (zhihu.com)</a></p>
<h4 id="数据绑定dom原理："><a href="#数据绑定dom原理：" class="headerlink" title="数据绑定dom原理："></a>数据绑定dom原理：</h4><p>监听一项数据：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="title class_">Object</span>.<span class="title function_">defineProperty</span>(<span class="variable language_">this</span>.<span class="property">$data</span>, key, &#123;</span><br><span class="line">  <span class="attr">get</span>: <span class="keyword">function</span>(<span class="params"></span>)&#123;</span><br><span class="line">    dep.<span class="title function_">depend</span>(); <span class="comment">// 这里进行依赖收集</span></span><br><span class="line">    <span class="keyword">return</span> value;</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="attr">set</span>: <span class="keyword">function</span>(<span class="params">newValue</span>)&#123;</span><br><span class="line">    value = newValue;</span><br><span class="line">    <span class="comment">// self.render();</span></span><br><span class="line">    dep.<span class="title function_">notify</span>();  <span class="comment">// 这里进行virtualDom更新，通知需要更新的组件render</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<p>监听所有数据：递归的设置<code>set</code>和<code>get</code>来监听</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">vue.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">observer</span> = <span class="keyword">function</span>(<span class="params">obj</span>)&#123;</span><br><span class="line">    <span class="keyword">var</span> value;</span><br><span class="line">  <span class="keyword">var</span> self = <span class="variable language_">this</span>;</span><br><span class="line">  <span class="keyword">for</span>(<span class="keyword">var</span> key <span class="keyword">in</span> obj)&#123;  <span class="comment">// 递归设置set和get</span></span><br><span class="line">    value = obj[key];</span><br><span class="line">    <span class="keyword">if</span>(<span class="keyword">typeof</span> value === <span class="string">&#x27;object&#x27;</span>)&#123;</span><br><span class="line">      <span class="variable language_">this</span>.<span class="title function_">observer</span>(value);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="title class_">Object</span>.<span class="title function_">defineProperty</span>(<span class="variable language_">this</span>.<span class="property">$data</span>, key, &#123;</span><br><span class="line">        <span class="attr">get</span>: <span class="keyword">function</span>(<span class="params"></span>)&#123;</span><br><span class="line">          <span class="keyword">return</span> value;</span><br><span class="line">        &#125;,</span><br><span class="line">        <span class="attr">set</span>: <span class="keyword">function</span>(<span class="params">newValue</span>)&#123;</span><br><span class="line">          value = newValue;</span><br><span class="line">          self.<span class="title function_">render</span>();</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>用利Object.defineProperty设置data的setter, 修改数据->get到当前数据相关依赖的dom节点，更新相应的部分虚拟dom，然后vue对比虚拟dom和dom，合并。</p>
<p>其实也是一种发布者-订阅者的模式，Promise的实现就是这种模式。</p>
<p><img src="/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220222221123419.png" alt="image-20220222221123419"></p>
<p>[如何理解vue数据双向绑定原理 - 知乎 (zhihu.com)</p>
<p><a target="_blank" rel="noopener" href="https://zhuanlan.zhihu.com/p/138710460"><a target="_blank" rel="noopener" href="https://blog.csdn.net/weixin_42335036/article/details/109286637">(28条消息) vue 源码分析，实现原理（Watcher, Dep, computed）_左不言的博客-CSDN博客_vue watcher源码</a></a></p>
<p><img src="/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220222233513540.png" alt="image-20220222233513540"></p>
<p><strong>dep</strong>：一个类，用于记录watcher与observer，dep.subs[]存放所有的监听的watcher,使用dep.notify()方法通知所有的watcher。每个属性都有一个自身的 dep。</p>
<p><strong>Observer</strong>是一个数据监听器，其实现核心方法就是Object.defineProperty( )</p>
<p><strong>Watcher</strong>就是一个订阅者。用于将Observer发来的update消息处理，执行Watcher绑定的更新函数。</p>
<p><strong>Compile</strong>主要的作用是把new SelfVue 绑定的dom节点，（也就是el标签绑定的id）遍历该节点的所有子节点，找出其中所有的v-指令和双括号 .</p>
<ul>
<li>（1）如果子节点含有v-指令，即是元素节点，则对这个元素添加监听事件。（如果是v-on，则<code>node.addEventListener(&#39;click&#39;）</code>，如果是v-model，则<code>node.addEventListener(&#39;input&#39;))</code>。接着初始化模板元素，创建一个Watcher绑定这个元素节点。</li>
<li>（2）如果子节点是文本节点，即"  ",则用正则表达式取出"  "中的data，然后<code>var initText = this.vm[exp]</code>，用initText去替代其中的data。</li>
</ul>
<blockquote>
<p>编译html模板时，发现需要特殊处理的变量，比如v-model=“name”，这个name被发现以后，就准备为其创建watcher，在创建watcher的时候，先把这个watcher挂载到Dep.target这个全局静态变量上，然后触发一次get事件，这样就触发了get函数中的Dep.target && dep.addSub(Dep.target);，等get到了变量以后，也已经添加到subs队列里了，这时候在令Dep.target = null。</p>
</blockquote>
<h4 id="数组的处理"><a href="#数组的处理" class="headerlink" title="数组的处理"></a>数组的处理</h4><p>数组不能用<code>Object.defineProperty</code>来处理，应该怎么办呢？Vue里面操作数组，直接用下标更改，是没有用的，必须使用<code>push</code>, <code>shift</code>等方法来操作</p>
<p>其实Vue用装饰者模式来重写了数组这些方法</p>
<p>Vue使用Object.create，创建了自己的新的数组对象，新的数组对象的__proto__指向了原生Array.prototype，这样，新Array可以通过原型链得到Array的所有属性和方法，同时可以自己在这个数组对象上添加新方法，并且不会污染到原生Array。</p>
<p>Vue在自己的数组原型上添加了push,shift，sort,reverse,push等方法，里面添加了</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">dep.<span class="title function_">notify</span>();</span><br></pre></td></tr></table></figure>

<p>这句话，使得可以数组变化也能被侦听到。</p>
<h4 id="v-model"><a href="#v-model" class="headerlink" title="v-model"></a>v-model</h4><p>通过@input将数据变化通知给data,同时data绑定到input的dom上（）</p>
<p>v-model=@input+v-on:</p>
<h4 id="Watch："><a href="#Watch：" class="headerlink" title="Watch："></a>Watch：</h4><p>watch 在一开始初始化的时候，会 <strong>读取</strong> 一遍 监听的数据的值，于是，此时 那个数据就收集到 watch 的 watcher 了</p>
<p>然后 你给 watch 设置的 handler ，watch 会放入 watcher 的更新函数中</p>
<p>当 数据改变时，通知 watch 的 watcher 进行更新，于是 你设置的 handler 就被调用了</p>
<h4 id="Computed"><a href="#Computed" class="headerlink" title="Computed"></a>Computed</h4><p><a target="_blank" rel="noopener" href="https://zhuanlan.zhihu.com/p/53219652">【Vue原理】月老Computed - 白话版 - 知乎 (zhihu.com)</a></p>
<p><strong>1</strong>computed 通过 watcher.dirty 控制是否读取缓存</p>
<p><strong>2</strong>computed 会让 【<strong>data依赖</strong>】 收集到 【<strong>依赖computed的watcher</strong>】，从而 data 变化时，会同时通知 computed 和 依赖computed的地方</p>
<h4 id="Vue-router"><a href="#Vue-router" class="headerlink" title="Vue-router"></a>Vue-router</h4><p>vue-router的工作流程有如下几步</p>
<ol>
<li><p>url改变</p>
</li>
<li><p>触发url改变事件，又分为两类，history模式和hash模式</p>
<blockquote>
<p>hash模式的值可以通过<code>location.hash</code>拿到，监听改变可以使用<code>onhashchange</code>事件；history的值可以用<code>location.pathname</code>拿到，可以用<code>onpopstate</code>事件来监听改变。</p>
<p>hash就是指url尾巴后的#号以及后面的字符，由于hash值的变化不会导致浏览器向服务器发出请求，而且hash改变会触发onhashchange事件。</p>
<p>hash的特点：</p>
<p>hash虽然出现url中，但<strong>不会被包含在HTTP请求中，对后端完全没有影响</strong>，因此改变hash不会重新加载页面。hash本来拿来做页面定位的，如果拿来做路由的话，原来<strong>锚点功能就不能用了</strong>。hash模式又叫做前端路由，因为改变hash值不会向后端发送请求</p>
</blockquote>
</li>
<li><p>改变vue-router里的current变量</p>
</li>
<li><p>监听current变量，current改变后获取相应的组件</p>
</li>
<li><p>render新组件</p>
</li>
</ol>
<p>vue-router挂载到vue上依靠vue.use方法，运行vue-router的install方法，vue-router的install方法利用vue.mixin方法，将一些变量和方法全局混入Vue的实例</p>
<p><a target="_blank" rel="noopener" href="http://www.dennisgo.cn/Articles/Node/Node.html">Node.js · 前端进阶 (dennisgo.cn)</a></p>
<p>导出对象：moudle.exports=obj</p>
<p><code>require</code>和<code>module.exports</code>干的事情并不复杂，我们先假设有一个全局对象<code>&#123;&#125;</code>，初始情况下是空的，当你<code>require</code>某个文件时，就将这个文件拿出来执行，如果这个文件里面存在<code>module.exports</code>，当运行到这行代码时将<code>module.exports</code>的值加入这个对象，键为对应的文件名，最终这个对象就长这样：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="string">&quot;a.js&quot;</span>: <span class="string">&quot;hello world&quot;</span>,</span><br><span class="line">  <span class="string">&quot;b.js&quot;</span>: <span class="keyword">function</span> <span class="title function_">add</span>(<span class="params"></span>)&#123;&#125;,</span><br><span class="line">  <span class="string">&quot;c.js&quot;</span>: <span class="number">2</span>,</span><br><span class="line">  <span class="string">&quot;d.js&quot;</span>: &#123; <span class="attr">num</span>: <span class="number">2</span> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>require加载顺序</p>
<blockquote>
<ol>
<li>优先加载内置模块，即使有同名文件，也会优先使用内置模块。</li>
<li>不是内置模块，先去缓存找。</li>
<li>缓存没有就去找对应路径的文件。</li>
<li>不存在对应的文件，就将这个路径作为文件夹加载。</li>
<li>对应的文件和文件夹都找不到就去<code>node_modules</code>下面找。</li>
<li>还找不到就报错了。</li>
</ol>
</blockquote>
<p>简单的http服务器：</p>
<p>搭建一个简单的<code>Web服务器</code>，使用原生的<code>http</code>模块就够了，一个简单的<code>Hello World</code>程序几行代码就够了：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> http = <span class="built_in">require</span>(<span class="string">&#x27;http&#x27;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> port = <span class="number">3000</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> server = http.<span class="title function_">createServer</span>(<span class="function">(<span class="params">req, res</span>) =&gt;</span> &#123;</span><br><span class="line">  res.<span class="property">statusCode</span> = <span class="number">200</span></span><br><span class="line">  res.<span class="title function_">setHeader</span>(<span class="string">&#x27;Content-Type&#x27;</span>, <span class="string">&#x27;text/plain&#x27;</span>)</span><br><span class="line">  res.<span class="title function_">end</span>(<span class="string">&#x27;Hello World&#x27;</span>)</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line">server.<span class="title function_">listen</span>(port, <span class="function">() =&gt;</span> &#123;</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`Server is running on http://127.0.0.1:<span class="subst">$&#123;port&#125;</span>/`</span>)</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>

<h3 id="Express框架"><a href="#Express框架" class="headerlink" title="Express框架"></a>Express框架</h3><p><a target="_blank" rel="noopener" href="http://www.dennisgo.cn/Articles/Node/Express.html">手写Express.js源码 · 前端进阶 (dennisgo.cn)</a></p>
<ol>
<li><code>Express</code>也是用原生API<code>http.createServer</code>来实现的。</li>
<li><code>Express</code>的主要工作是将<code>http.createServer</code>的回调函数拆出来了，构建了一个路由结构<code>Router</code>。</li>
<li>这个路由结构由很多层<code>layer</code>组成。</li>
<li>一个中间件就是一个<code>layer</code>。</li>
<li>整个路由匹配过程其实就是遍历<code>router.layer</code>的一个过程。</li>
<li>每个请求来了都会遍历一遍所有的<code>layer</code>，匹配上就执行回调，一个请求可能会匹配上多个<code>layer</code>。</li>
</ol>
<h3 id="KOA框架"><a href="#KOA框架" class="headerlink" title="KOA框架"></a>KOA框架</h3><p><strong><code>Koa</code>和<code>Express</code>的一个重大区别：<code>Express</code>使用老的API，兼容性更强，可以在老的<code>Node.js</code>版本上运行；<code>Koa</code>因为使用了新API，只能在<code>v7.6.0</code>或者更高版本上运行了。</strong></p>
<ol>
<li><code>Express</code>的思路是大而全，内置了很多功能，比如路由，静态资源等，而且<code>Express</code>的中间件也是使用路由同样的机制实现的，整个代码更复杂。<code>Express</code>源码可以看我之前这篇文章：<a target="_blank" rel="noopener" href="https://juejin.im/post/6890358903960240142">手写Express.js源码</a></li>
<li><code>Koa</code>的思路看起来更清晰，<code>Koa</code>本身的库只是一个内核，只有中间件功能，来的请求会依次经过每一个中间件，然后再出来返回给请求者，这就是大家经常听说的“<strong>洋葱模型</strong>”。</li>
</ol>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//创建koa对象</span></span><br><span class="line"><span class="keyword">const</span> koa = <span class="built_in">require</span>(<span class="string">&#x27;koa&#x27;</span>)</span><br><span class="line"><span class="keyword">const</span> app = <span class="keyword">new</span> <span class="title function_">koa</span>()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//绑定中间件</span></span><br><span class="line"><span class="keyword">const</span> durationMiddleware = <span class="built_in">require</span>(<span class="string">&#x27;./middleware/koa_reponse_duration&#x27;</span>)</span><br><span class="line">app.<span class="title function_">use</span>(durationMiddleware)</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> headerMiddleware = <span class="built_in">require</span>(<span class="string">&#x27;./middleware/koa_reponse_header&#x27;</span>)</span><br><span class="line">app.<span class="title function_">use</span>(headerMiddleware)</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> dataMiddleware = <span class="built_in">require</span>(<span class="string">&#x27;./middleware/koa_reponse_data&#x27;</span>)</span><br><span class="line">app.<span class="title function_">use</span>(dataMiddleware)</span><br><span class="line"></span><br><span class="line"><span class="comment">//绑定端口号</span></span><br><span class="line">app.<span class="title function_">listen</span>(<span class="number">3000</span>)</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;电商可视化后端服务已经启动在3000端口!&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> wsService = <span class="built_in">require</span>(<span class="string">&#x27;./service/websocket_service&#x27;</span>)</span><br><span class="line"></span><br><span class="line">wsService.<span class="title function_">listen</span>()</span><br></pre></td></tr></table></figure>

<p>koa想实现<strong>路由</strong>，需要require并use一个<strong>koa-router</strong>中间件，注意，它也是一个中间件</p>
<p>koa想实现<strong>静态资源</strong>，就require并use一个<strong>koa-static</strong>中间件</p>
<h2 id="37-设计模式"><a href="#37-设计模式" class="headerlink" title="37.设计模式"></a>37.设计模式</h2><h3 id="1-发布者-订阅者模式"><a href="#1-发布者-订阅者模式" class="headerlink" title="1.发布者-订阅者模式"></a>1.发布者-订阅者模式</h3><img src="%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220223120503596.png" alt="image-20220223120503596" style="zoom:50%;" />

<p>Promise、Vue中的双向变量绑定、处理异步</p>
<blockquote>
<ol>
<li>消息中心：负责存储消息与订阅者的对应关系，有消息触发时，负责通知订阅者</li>
<li>订阅者：去消息中心订阅自己感兴趣的消息</li>
<li>发布者：满足条件时，通过消息中心发布消息</li>
</ol>
</blockquote>
<p>有了这种模式，前面处理几个相互依赖的异步API就不用陷入"回调地狱"了，只需要让后面的订阅前面的成功消息，前面的成功后发布消息就行了。</p>
<p>自己实现</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">PubSub</span> &#123;</span><br><span class="line">  <span class="title function_">constructor</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="comment">// 一个对象存放所有的消息订阅</span></span><br><span class="line">    <span class="comment">// 每个消息对应一个数组，数组结构如下</span></span><br><span class="line">    <span class="comment">// &#123;</span></span><br><span class="line">    <span class="comment">//   &quot;event1&quot;: [cb1, cb2]</span></span><br><span class="line">    <span class="comment">// &#125;</span></span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">events</span> = &#123;&#125;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="title function_">subscribe</span>(<span class="params">event, callback</span>) &#123;</span><br><span class="line">    <span class="keyword">if</span>(<span class="variable language_">this</span>.<span class="property">events</span>[event]) &#123;</span><br><span class="line">      <span class="comment">// 如果有人订阅过了，这个键已经存在，就往里面加就好了</span></span><br><span class="line">      <span class="variable language_">this</span>.<span class="property">events</span>[event].<span class="title function_">push</span>(callback);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="comment">// 没人订阅过，就建一个数组，回调放进去</span></span><br><span class="line">      <span class="variable language_">this</span>.<span class="property">events</span>[event] = [callback]</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="title function_">publish</span>(<span class="params">event, ...args</span>) &#123;</span><br><span class="line">    <span class="comment">// 取出所有订阅者的回调执行</span></span><br><span class="line">    <span class="keyword">const</span> subscribedEvents = <span class="variable language_">this</span>.<span class="property">events</span>[event];</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>(subscribedEvents &amp;&amp; subscribedEvents.<span class="property">length</span>) &#123;</span><br><span class="line">      subscribedEvents.<span class="title function_">forEach</span>(<span class="function"><span class="params">callback</span> =&gt;</span> &#123;</span><br><span class="line">        callback.<span class="title function_">call</span>(<span class="variable language_">this</span>, ...args);</span><br><span class="line">      &#125;);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="title function_">unsubscribe</span>(<span class="params">event, callback</span>) &#123;</span><br><span class="line">    <span class="comment">// 删除某个订阅，保留其他订阅</span></span><br><span class="line">    <span class="keyword">const</span> subscribedEvents = <span class="variable language_">this</span>.<span class="property">events</span>[event];</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>(subscribedEvents &amp;&amp; subscribedEvents.<span class="property">length</span>) &#123;</span><br><span class="line">      <span class="variable language_">this</span>.<span class="property">events</span>[event] = <span class="variable language_">this</span>.<span class="property">events</span>[event].<span class="title function_">filter</span>(<span class="function"><span class="params">cb</span> =&gt;</span> cb !== callback)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="2-工厂模式-封装代码"><a href="#2-工厂模式-封装代码" class="headerlink" title="2. 工厂模式-封装代码"></a>2. 工厂模式-封装代码</h3><p><strong>应用-jquery</strong></p>
<p>工厂模式的名字就很直白，封装的模块就像一个工厂一样批量的产出需要的对象。常见工厂模式的一个特征就是调用的时候不需要使用<code>new</code>，而且传入的参数比较简单。但是调用次数可能比较频繁，经常需要产出不同的对象，频繁调用时不用<code>new</code>也方便很多。一个工厂模式的代码结构如下所示:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">(<span class="keyword">function</span>(<span class="params"></span>)&#123;										<span class="comment">//自执行</span></span><br><span class="line">     <span class="keyword">function</span> <span class="title function_">popup</span>(<span class="params">type, content, color</span>) &#123;</span><br><span class="line">    <span class="keyword">if</span>(<span class="variable language_">this</span> <span class="keyword">instanceof</span> popup) &#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">new</span> <span class="variable language_">this</span>[type](content, color);		<span class="comment">//内部帮我们new好</span></span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">new</span> <span class="title function_">popup</span>(type, content, color);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  popup.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">infoPopup</span> = <span class="keyword">function</span>(<span class="params">content, color</span>) &#123;&#125;</span><br><span class="line">  popup.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">confirmPopup</span> = <span class="keyword">function</span>(<span class="params">content, color</span>) &#123;&#125;</span><br><span class="line">  popup.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">cancelPopup</span> = <span class="keyword">function</span>(<span class="params">content, color</span>) &#123;&#125;</span><br><span class="line"></span><br><span class="line">  <span class="variable language_">window</span>.<span class="property">popup</span> = popup;								<span class="comment">//帮我们挂载到window上</span></span><br><span class="line">&#125;)()</span><br><span class="line"></span><br><span class="line"><span class="comment">// 外面就直接可以使用popup模块了</span></span><br><span class="line"><span class="keyword">let</span> infoPopup1 = <span class="title function_">popup</span>(<span class="string">&#x27;infoPopup&#x27;</span>, content, color);</span><br></pre></td></tr></table></figure>

<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="title class_">JQuery</span></span><br><span class="line"></span><br><span class="line">(<span class="keyword">function</span>(<span class="params"></span>)&#123;</span><br><span class="line">  <span class="keyword">var</span> jQuery = <span class="keyword">function</span>(<span class="params">selector</span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> jQuery.<span class="property">fn</span>.<span class="title function_">init</span>(selector);   <span class="comment">// new一下init, init才是真正的构造函数</span></span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  jQuery.<span class="property">fn</span> = jQuery.<span class="property"><span class="keyword">prototype</span></span>;     <span class="comment">// jQuery.fn就是jQuery.prototype的简写</span></span><br><span class="line"></span><br><span class="line">  jQuery.<span class="property">fn</span>.<span class="property">init</span> = <span class="keyword">function</span>(<span class="params">selector</span>) &#123;</span><br><span class="line">    <span class="comment">// 这里面实现真正的构造函数</span></span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 让init和jQuery的原型指向同一个对象，便于挂载实例方法</span></span><br><span class="line">  jQuery.<span class="property">fn</span>.<span class="property">init</span>.<span class="property"><span class="keyword">prototype</span></span> = jQuery.<span class="property">fn</span>;  </span><br><span class="line"></span><br><span class="line">  <span class="comment">// 最后将jQuery挂载到window上</span></span><br><span class="line">  <span class="variable language_">window</span>.<span class="property">$</span> = <span class="variable language_">window</span>.<span class="property">jQuery</span> = jQuery;</span><br><span class="line">&#125;)();</span><br></pre></td></tr></table></figure>

<h3 id="3-建造者模式"><a href="#3-建造者模式" class="headerlink" title="3. 建造者模式"></a>3. 建造者模式</h3><p>用于比较复杂的大对象的构建，比如<code>Vue</code>，<code>Vue</code>内部包含一个功能强大，逻辑复杂的对象，在构建的时候也需要传很多参数进去。像这种需要创建的情况不多，创建的对象本身又很复杂的时候就适用建造者模式</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">Model1</span>(<span class="params"></span>) &#123;&#125;   <span class="comment">// 模块1</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">Model2</span>(<span class="params"></span>) &#123;&#125;   <span class="comment">// 模块2</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 最终使用的类</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">Final</span>(<span class="params"></span>) &#123;</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">model1</span> = <span class="keyword">new</span> <span class="title class_">Model1</span>();</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">model2</span> = <span class="keyword">new</span> <span class="title class_">Model2</span>();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用时</span></span><br><span class="line"><span class="keyword">var</span> obj = <span class="keyword">new</span> <span class="title class_">Final</span>();</span><br></pre></td></tr></table></figure>

<p>上述代码中我们最终使用的是<code>Final</code>，但是<code>Final</code>里面的结构比较复杂，有很多个子模块，<code>Final</code>就是将这些子模块组合起来完成功能，这种需要精细化构造的就适用于建造者模式。</p>
<h3 id="4-单例模式"><a href="#4-单例模式" class="headerlink" title="4. 单例模式"></a>4. 单例模式</h3><p>适用于全局只能有一个实例对象的场景，单例模式的一般结构</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">Singleton</span>(<span class="params"></span>) &#123;&#125;</span><br><span class="line"></span><br><span class="line"><span class="title class_">Singleton</span>.<span class="property">getInstance</span> = <span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">  <span class="keyword">if</span>(<span class="variable language_">this</span>.<span class="property">instance</span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">instance</span>;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">instance</span> = <span class="keyword">new</span> <span class="title class_">Singleton</span>();</span><br><span class="line">  <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">instance</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>应用：<strong>Vue-router</strong>、在项目中用websocket时，只想有一个ws的实例对象</p>
<p>用一个变量来标记代码是否已经执行过了，如果执行过了就返回上次的执行结果</p>
<h3 id="5-原型模式"><a href="#5-原型模式" class="headerlink" title="5. 原型模式"></a>5. 原型模式</h3><p>应用就是JS本身</p>
<p>使用<code>Object.create</code>指定一个对象作为原型来创建对象:</p>
<p>JS的面向对象其实更加是这种模式的体现</p>
<p>让子类<code>Child.prototype.__proto__</code>的指向父类的<code>prototype</code>从而获取父类的方法和属性</p>
<h4 id="2-4-总结"><a href="#2-4-总结" class="headerlink" title="2-4 总结"></a>2-4 总结</h4><ol>
<li>需要大量产生类似实例的组件可以考虑用工厂模式来封装。</li>
<li>内部逻辑较复杂，外部使用时需要的实例也不多，可以考虑用建造者模式来封装。</li>
<li>全局只能有一个实例的需要用单例模式来封装。</li>
<li>新老对象之间可能有继承关系的可以考虑用原型模式来封装，JS本身就是一个典型的原型模式。</li>
</ol>
<h2 id="38-工程化"><a href="#38-工程化" class="headerlink" title="38. 工程化"></a>38. 工程化</h2><h3 id="webpack"><a href="#webpack" class="headerlink" title="webpack"></a>webpack</h3><h2 id="39-CSS常见问题"><a href="#39-CSS常见问题" class="headerlink" title="39. CSS常见问题"></a>39. CSS常见问题</h2><h3 id="1-流的定义"><a href="#1-流的定义" class="headerlink" title="1.流的定义"></a>1.流的定义</h3><p>块元素独占一行，自上而下进行排列，行内元素和行内块元素不会独占一行，这就是流</p>
<h3 id="1-浮动"><a href="#1-浮动" class="headerlink" title="1. 浮动"></a>1. 浮动</h3><p><strong>让某个div****元素脱离标准流，漂浮在标准流之上，和标准流不是一个层次。</strong></p>
<img src="%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220223223123620.png" alt="image-20220223223123620" style="zoom:33%;" />

<p><img src="/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220223223221672.png" alt="image-20220223223221672"></p>
<p>第一个浮动元素位于其原本位置，所有标准流元素找上一个标准流元素，其他浮动元素找上一个浮动元素</p>
<h4 id="和绝对定位的区别"><a href="#和绝对定位的区别" class="headerlink" title="和绝对定位的区别"></a>和绝对定位的区别</h4><p>绝对定位是将元素彻底从文档流删除，该元素再也不会影响其他元素的布局了</p>
<p>浮动虽然脱离了标准流，但还是会影响布局，比如文字环绕，浮动的元素好像不存在，但是文字不会占据那个位置。</p>
<p>绝对定位：</p>
<img src="%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220223224015953.png" alt="image-20220223224015953" style="zoom: 50%;" />

<p>浮动：</p>
<img src="%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220223224050208.png" alt="image-20220223224050208" style="zoom:50%;" />

<h4 id="清除浮动"><a href="#清除浮动" class="headerlink" title="清除浮动"></a>清除浮动</h4><p>方法一：</p>
<p>​    在要清除浮动的元素身后的元素添加：<strong>clear:both</strong>(/left/right)</p>
<p>​    这就有三种方式：</p>
<pre><code>1. 添加一个空盒子设置clear：both
2. 给后面的盒子设置clear：both
3. 给浮动的盒子添加一个类，类给当前盒子添加一个**伪元素:after**，然后设置伪元素的属性为clear：both
</code></pre>
<p>​        </p>
<p>方法二：</p>
<p>​    触发父盒子的<strong>BFC</strong>：</p>
<p>​    BFC(Block formatting context)直译为”块级格式化上下文”。它是一个独立的渲染区域，只有Block-level box参与， 它规定了内部的Block-level Box如何布局，并且与这个区域外部毫不相干。</p>
<p>这样，盒子内的浮动就无法影响外部。    </p>
<p>​        触发BFC的方式：</p>
<blockquote>
<ul>
<li><p>根元素</p>
</li>
<li><p>float的值不为none</p>
</li>
<li><p>overflow的值不为visible</p>
</li>
<li><p>display的值为inline-block、table-cell、table-caption</p>
</li>
<li><p>position的值为absolute或fixed</p>
</li>
</ul>
</blockquote>
<h3 id="3-重绘和重排"><a href="#3-重绘和重排" class="headerlink" title="3. 重绘和重排"></a>3. 重绘和重排</h3><p>重绘：重绘是指一个<strong>元素外观的改变</strong>所触发的浏览器行为，</p>
<p>重排：渲染树的一部分(或全部)因为元素的<strong>规模尺寸，布局，隐藏</strong>等改变而需要<strong>重新构建</strong>,</p>
<p>​    又名重构/<strong>回流</strong>/reflow</p>
<p>重构一封重绘，重绘不一定重构。</p>
<p><strong>减少重绘重排的方法</strong></p>
<ul>
<li>对于多次重排的元素，比如<strong>动画</strong>。<strong>使用绝对定位分开文档流</strong>，使其不影响其他元素</li>
<li>使用className一次性更改属性，直接加类或者删掉类，而不是一点一点更改某一项属性</li>
</ul>
<h3 id="4-哪些属性是可继承的"><a href="#4-哪些属性是可继承的" class="headerlink" title="4. 哪些属性是可继承的"></a>4. 哪些属性是可继承的</h3><p> <strong>可继承的属性</strong>：</p>
<p>1.字体系列属性：font, font-family, font-weight, font-size, font-style, font-variant, font-stretch, font-size-adjust…</p>
<p>2.文本系列属性：text-indent, text-align, line-height, word-spacing, letter-spacing, direction, color…</p>
<p>3.元素可见性：visibility</p>
<p> <strong>不可继承的属性</strong>：</p>
<p>1、display</p>
<p>2、文本属性：vertical-align,text-decoration,text-shadow,white-space,unicode-bidi</p>
<p>3、盒子模型的属性: 宽度、高度、内外边距、边框等</p>
<p>4、背景属性：背景图片、颜色、位置等</p>
<p>5、定位属性：浮动、清除浮动、定位position等</p>
<h2 id="40-跨域"><a href="#40-跨域" class="headerlink" title="40. 跨域"></a>40. 跨域</h2><h3 id="1-如何解决跨域"><a href="#1-如何解决跨域" class="headerlink" title="1. 如何解决跨域"></a>1. 如何解决跨域</h3><h4 id="1、JSONP跨域"><a href="#1、JSONP跨域" class="headerlink" title="1、JSONP跨域"></a>1、JSONP跨域</h4><p><strong>jsonp</strong>的原理就是利用<code>&lt;script&gt;</code>标签没有跨域限制(img的src、link的href也没有)，通过<code>&lt;script&gt;</code>标签src属性，发送带有callback参数的GET请求，服务端将接口返回数据拼凑到callback函数中，返回给浏览器，浏览器解析执行，从而前端拿到callback函数返回的数据。</p>
<p>关键在于，前后端要协商好函数的名称，服务端返回时需要将data包裹在协商好的函数里，这样前端收到数据会自动执行这个函数。</p>
<p>1）原生JS实现：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">&lt;script&gt;</span><br><span class="line">   <span class="keyword">var</span> script = <span class="variable language_">document</span>.<span class="title function_">createElement</span>(<span class="string">&#x27;script&#x27;</span>);</span><br><span class="line">   script.<span class="property">type</span> = <span class="string">&#x27;text/javascript&#x27;</span>;</span><br><span class="line"></span><br><span class="line">   <span class="comment">// 传参一个回调函数名给后端，方便后端返回时执行这个在前端定义的回调函数</span></span><br><span class="line">   script.<span class="property">src</span> = <span class="string">&#x27;http://www.domain2.com:8080/login?user=admin&amp;callback=handleCallback&#x27;</span>;</span><br><span class="line">   <span class="variable language_">document</span>.<span class="property">head</span>.<span class="title function_">appendChild</span>(script);</span><br><span class="line"></span><br><span class="line">   <span class="comment">// 回调执行函数</span></span><br><span class="line">   <span class="keyword">function</span> <span class="title function_">handleCallback</span>(<span class="params">res</span>) &#123;</span><br><span class="line">       <span class="title function_">alert</span>(<span class="title class_">JSON</span>.<span class="title function_">stringify</span>(res));</span><br><span class="line">   &#125;</span><br><span class="line">&lt;/script&gt;</span><br></pre></td></tr></table></figure>

<p>服务端返回如下（返回时即执行全局函数）：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">router.<span class="title function_">get</span>(<span class="string">&#x27;/article-list&#x27;</span>, <span class="function">(<span class="params">req, res</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(req.<span class="property">query</span>, <span class="string">&#x27;123&#x27;</span>);</span><br><span class="line">  <span class="keyword">let</span> data = &#123;</span><br><span class="line">    <span class="attr">message</span>: <span class="string">&#x27;success!&#x27;</span>,</span><br><span class="line">    <span class="attr">name</span>: req.<span class="property">query</span>.<span class="property">name</span>,</span><br><span class="line">    <span class="attr">age</span>: req.<span class="property">query</span>.<span class="property">age</span></span><br><span class="line">  &#125;</span><br><span class="line">  data = <span class="title class_">JSON</span>.<span class="title function_">stringify</span>(data)</span><br><span class="line">  res.<span class="title function_">end</span>(<span class="string">&#x27;handleCallback(&#x27;</span> + data + <span class="string">&#x27;)&#x27;</span>);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<p>2）jquery Ajax实现：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">$.<span class="title function_">ajax</span>(&#123;</span><br><span class="line">    <span class="attr">url</span>: <span class="string">&#x27;http://www.domain2.com:8080/login&#x27;</span>,</span><br><span class="line">    <span class="attr">type</span>: <span class="string">&#x27;get&#x27;</span>,</span><br><span class="line">    <span class="attr">dataType</span>: <span class="string">&#x27;jsonp&#x27;</span>,  <span class="comment">// 请求方式为jsonp</span></span><br><span class="line">    <span class="attr">jsonpCallback</span>: <span class="string">&quot;handleCallback&quot;</span>,  <span class="comment">// 自定义回调函数名</span></span><br><span class="line">    <span class="attr">data</span>: &#123;&#125;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<p>3）Vue axios实现：(已经封装好了，直接axios.jsonp就可以)</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable language_">this</span>.<span class="property">$http</span> = axios;</span><br><span class="line"><span class="variable language_">this</span>.<span class="property">$http</span>.<span class="title function_">jsonp</span>(<span class="string">&#x27;http://www.domain2.com:8080/login&#x27;</span>, &#123;</span><br><span class="line">    <span class="attr">params</span>: &#123;&#125;,</span><br><span class="line">    <span class="attr">jsonp</span>: <span class="string">&#x27;handleCallback&#x27;</span></span><br><span class="line">&#125;).<span class="title function_">then</span>(<span class="function">(<span class="params">res</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(res); </span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>

<h4 id="2-跨域资源共享（CORS）"><a href="#2-跨域资源共享（CORS）" class="headerlink" title="2. 跨域资源共享（CORS）"></a>2. 跨域资源共享（CORS）</h4><p>将CORS跨域请求分为<strong>简单请求</strong>和<strong>非简单请求</strong>。</p>
<p>只要同时满足一下两个条件，就属于<strong>简单请求</strong></p>
<p>(1)使用下列方法之一：</p>
<ul>
<li>head</li>
<li>get</li>
<li>post</li>
</ul>
<p>(2)请求的Heder是</p>
<ul>
<li>Accept</li>
<li>Accept-Language</li>
<li>Content-Language</li>
<li>Content-Type: 只限于三个值：application/x-www-form-urlencoded、multipart/form-data、text/plain</li>
</ul>
<p>不同时满足上面的两个条件，就属于非简单请求。浏览器对这两种的处理，是不一样的。</p>
<p>简单请求</p>
<p><strong>对于简单请求，浏览器直接发出CORS请求</strong>。具体来说，就是在头信息之中，增加一个Origin字段。</p>
<figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">GET /cors HTTP/1.1</span><br><span class="line">Origin: http://api.bob.com</span><br><span class="line">Host: api.alice.com</span><br><span class="line">Accept-Language: en-US</span><br><span class="line">Connection: keep-alive</span><br><span class="line">User-Agent: Mozilla/5.0...</span><br></pre></td></tr></table></figure>

<p>然后服务端设置<strong>Access-Control-Allow-Origin</strong>字段指定允许跨域的源，也可以设置<strong>Access-Control-Allow-Credential</strong>允许携带cookie</p>
<h4 id="3-代理服务器"><a href="#3-代理服务器" class="headerlink" title="3.代理服务器"></a>3.代理服务器</h4><p>利用服务器之间不存在跨域的特性，由代理服务器发请求</p>
<p>自己写一个nodejs代理服务器，vue中用webpack的代理服务器，上线以后可以用nginx代理</p>
<h4 id="4-iframe跨域-document-domain"><a href="#4-iframe跨域-document-domain" class="headerlink" title="4. iframe跨域+document.domain"></a>4. iframe跨域+document.domain</h4><p>适用于主域相同，子域不同的跨域应用场景，通过js强行把子域设置成主域，实现同域，再用cros跨域。</p>
<h4 id="5-websocket协议跨域"><a href="#5-websocket协议跨域" class="headerlink" title="5. websocket协议跨域"></a>5. websocket协议跨域</h4><p>websocket可以直接跨域</p>
<p>另外还有</p>
<h4 id="总结-1"><a href="#总结-1" class="headerlink" title="总结"></a>总结</h4><p>jsonp（只支持get请求，支持老的IE浏览器）适合加载不同域名的js、css，img等静态资源；CORS（支持所有类型的HTTP请求，但浏览器IE10以下不支持）适合做ajax各种跨域请求；Nginx代理跨域和nodejs中间件跨域原理都相似，都是搭建一个服务器，直接在服务器端请求HTTP接口，这适合前后端分离的前端项目调后端接口。document.domain+iframe适合主域名相同，子域名不同的跨域请求。postMessage、websocket都是HTML5新特性，兼容性不是很好，只适用于主流浏览器和IE10+。</p>
<h3 id="2-如何在跨域时携带cookie"><a href="#2-如何在跨域时携带cookie" class="headerlink" title="2. 如何在跨域时携带cookie"></a>2. 如何在跨域时携带cookie</h3><p><strong>法1：</strong></p>
<p>后台设置“Access-Control-Allow-Origin”但是不能设置为*，然后“Access-Control-Allow-Credentials”为true，ajax里添加withCredentials: true字段</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> service = axios.<span class="title function_">create</span>(&#123;</span><br><span class="line">  <span class="attr">baseURL</span>: process.<span class="property">env</span>.<span class="property">VUE_APP_BASE_API</span>, <span class="comment">// 环境变量base接口地址 url = base url + request url</span></span><br><span class="line">  <span class="attr">withCredentials</span>: <span class="literal">true</span>, <span class="comment">// 跨域请求时发送Cookie</span></span><br><span class="line">  <span class="attr">timeout</span>: <span class="number">60000</span>, <span class="comment">// 请求超时</span></span><br><span class="line">  <span class="attr">headers</span>: &#123;</span><br><span class="line">    <span class="string">&quot;Content-Type&quot;</span>: <span class="string">&quot;application/json; charset=UTF-8;&quot;</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>



<p><strong>法2</strong></p>
<p>配置代理。原理是服务器与服务器之间不存在跨域。develop阶段使用webpack的proxy代理，上线可以用ngnix反向代理。</p>
<p><strong>总结</strong>：方法一的cookie跨域携带需要在每一个请求里加上withCredentials凭证，可以针对性的针对某一个ajax请求去配置是否携带cookie，方法二的通过代理配置携带cookie会在我代理的每一个请求都把cookie携带上。方法二方法一都可以灵活的配置去为某一个请求携带cookie，在项目中由于做单点登录，需要整合7个厂家cookie信息，已达到每一次请求都刷新cookie时间，并且本着减少其他厂家工作量的原因，故此采用了nginx服务配置的方式去携带cookie。</p>
<h2 id="41-事件模型（冒泡和捕获）"><a href="#41-事件模型（冒泡和捕获）" class="headerlink" title="41. 事件模型（冒泡和捕获）"></a>41. 事件模型（冒泡和捕获）</h2><p><a target="_blank" rel="noopener" href="https://zhuanlan.zhihu.com/p/371332078">DOM事件模型：冒泡和捕获 - 知乎 (zhihu.com)</a></p>
<p>冒泡：里盒子到外盒子</p>
<p>捕获：外盒子到里盒子</p>
<p><strong>event.target 和 event.currentTarget</strong></p>
<p>event.target:触发事件的元素。event.currentTarget:绑定的元素</p>
<p>利用冒泡，实现事件委派，在最外层的盒子(event.currentTarget)设置click事件，点击里面的盒子，根据冒泡，可以拿到触发点击事件的元素(event.target)，然后可以对其进行操作。三级菜单就是这样实现的。</p>
<p><strong>event.stopPropagation()</strong></p>
<p>该方法用于<strong>阻止事件的冒泡</strong>，不让事件从子元素向父元素及上层元素冒泡，但是默认事件仍让会执行，比如a标签的跳转行为仍然会执行。</p>
<p>注意，使用 <strong>event.preventDefault()</strong> 可以取消事件的默认行为，但不能阻止事件冒泡，而使用 <strong>event.stopPropagation()</strong> 能阻止事件冒泡却<strong>不能取消事件的默认行为</strong>。如果既想取消事件的默认行为，又想阻止事件冒泡，则可以<strong>直接用 return false</strong>。</p>
<p><strong>event.stopImmediatePropagation()</strong></p>
<p>当使用 addEventListener 在<strong>同一个元素上绑定多个相同类型事件的事件监听函数</strong>时，比如先后绑定了click事件A 和 click事件B。</p>
<p>正常情况下，我点击元素会先触发事件A，然后再触发事件B，但有时候我就只想触发事件A，而不想触发事件B，那该怎么办呢？</p>
<p>这时就可以在事件A的处理函数中添加 event.stopImmediatePropagation() ，即可<strong>让剩下的监听函数不会被执行，包括事件冒泡行为</strong>。</p>
<h2 id="42-src与href的区别"><a href="#42-src与href的区别" class="headerlink" title="42. src与href的区别"></a>42. src与href的区别</h2><p>src用于替换当前元素，href用于在当前文档和引用资源之间确立联系。</p>
<p>src是source的缩写，指向外部资源的位置，指向的内容将会嵌入到文档中当前标签所在位置；在请求src资源时会将其指向的资源下载并应用到文档内，例如js脚本，img图片和frame等元素。当浏览器解析到该元素时，会暂停其他资源的下载和处理，直到将该资源加载、编译、执行完毕，图片和框架等元素也如此，类似于将所指向资源嵌入当前标签内。这也是为什么将js脚本放在底部而不是头部。</p>
<p>href是Hypertext Reference的缩写，指向网络资源所在位置，建立和当前元素（锚点）或当前文档（链接）之间的链接，如果我们在文档中添加</p>
<p>那么浏览器会识别该文档为css文件，就会<strong>并行下载</strong>资源并且不会停止对当前文档的处理。这也是为什么建议使用link方式来加载css，而不是使用@import方式。</p>
<p>简单来说就是<strong>src直接加载过来使用</strong>，但是<strong>href只是创造一个链接</strong>，可以使用，但是没有立即停下来加载，而是并行下载资源</p>
<h2 id="43-Vue中的key"><a href="#43-Vue中的key" class="headerlink" title="43. Vue中的key"></a>43. Vue中的key</h2><blockquote>
<p>key是给每一个vnode的唯一id，也是diff的一种优化策略，可以根据key，更准确， 更快的找到对应的vnode节点</p>
</blockquote>
<p>在Vue.js中，key是6个特殊属性key, ref, is, slot, slot-scope, scope其中之一。</p>
<p>key的值可以是number，也可以是string。</p>
<p>key主要作用于Vue的virtual DOM算法，在diff new nodes list和old nodes list时，作为识别VNode的一个线索。</p>
<p>如果不用key，Vue会用一种算法：<strong>最小化element的移动</strong>，就近复用并且会尝试尽最大程度在同适当的地方对相同类型的element，做patch或者reuse。,这样，添加了新元素的话，有可能导致事件绑定紊乱</p>
<p>比如原来数组是abcdef,这时候在中间插了个e，变成abcedef,e会直接就近占用原本c的元素，修改一些属性，但是c绑定的事件就绑定到了e上。</p>
<img src="%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220224184312276.png" alt="image-20220224184312276" style="zoom:50%;" />

<p>如果使用了key，Vue会根据keys的顺序记录element，<strong>曾经拥有了key的element如果不再出现的话，会被直接remove或者destoryed。不会占用其它的element</strong>，只会添加一个新的element</p>
<p>拥有同一个parent的children必须有unique keys。重复的key的导致render error。</p>
<p>由于Vue.js的obj和arr存在无法检测到数据变化的情况，obj是属性的新增和删除（原因是新增和删除都没有触发setter，watcher未告诉外界更新），arr则是数组内元素重新赋值或者修改length属性（原因是没有使用改变数组本身的方法，没有触发数组原型链拦截器，watcher未告诉外界更新）。 所以！通过赋予新key的方式，移除旧key div，渲染新key div，propObj和propArr在complexComponent组件内会重新触发一次生命周期，做一次重新渲染。<strong>此时父组件的propObj和propArr js变量其实已经获取到新值了，只是没有触发DOM也好，VNode也好的重新渲染。</strong>需要通过刷新key去force update，说到forceUpdate，可以通过$forceUpdate()去手动强制更新DOM。</p>
<h2 id="44-Vue和React对比"><a href="#44-Vue和React对比" class="headerlink" title="44. Vue和React对比"></a>44. Vue和React对比</h2><p>这里就做几个简单的类比吧，当然没有好坏之分，只是使用场景不同</p>
<p><strong>相同点</strong></p>
<ul>
<li>都有组件化思想</li>
<li>都支持服务器端渲染</li>
<li>都有Virtual DOM（虚拟dom）</li>
<li>数据驱动视图</li>
<li>都有支持native的方案：<code>Vue</code>的<code>weex</code>、<code>React</code>的<code>React native</code></li>
<li>都有自己的构建工具：<code>Vue</code>的<code>vue-cli</code>、<code>React</code>的<code>Create React App</code></li>
</ul>
<p><strong>区别</strong></p>
<ul>
<li>数据流向的不同。<code>react</code>从诞生开始就推崇单向数据流，而<code>Vue</code>是双向数据流</li>
<li>数据变化的实现原理不同。<code>react</code>使用的是不可变数据，而<code>Vue</code>使用的是可变的数据</li>
<li>组件化通信的不同。<code>react</code>中我们通过使用回调函数来进行通信的，而<code>Vue</code>中子组件向父组件传递消息有两种方式：事件和回调函数</li>
<li>diff算法不同。<code>react</code>主要使用diff队列保存需要更新哪些DOM，得到patch树，再统一操作批量更新DOM。<code>Vue</code> 使用双向指针，边对比，边更新DOM</li>
</ul>
<h2 id="45-单面应用的理解"><a href="#45-单面应用的理解" class="headerlink" title="45. 单面应用的理解"></a>45. 单面应用的理解</h2><h4 id="单页应用与多页应用的区别"><a href="#单页应用与多页应用的区别" class="headerlink" title="单页应用与多页应用的区别"></a>单页应用与多页应用的区别</h4><table>
<thead>
<tr>
<th align="left"></th>
<th align="left">单页面应用（SPA）</th>
<th align="left">多页面应用（MPA）</th>
</tr>
</thead>
<tbody><tr>
<td align="left">组成</td>
<td align="left">一个主页面和多个页面片段</td>
<td align="left">多个主页面</td>
</tr>
<tr>
<td align="left">刷新方式</td>
<td align="left">局部刷新</td>
<td align="left">整页刷新</td>
</tr>
<tr>
<td align="left">url模式</td>
<td align="left">哈希模式</td>
<td align="left">历史模式</td>
</tr>
<tr>
<td align="left">SEO搜索引擎优化</td>
<td align="left">难实现，可使用SSR方式改善</td>
<td align="left">容易实现</td>
</tr>
<tr>
<td align="left">数据传递</td>
<td align="left">容易</td>
<td align="left">通过url、cookie、localStorage等传递</td>
</tr>
<tr>
<td align="left">页面切换</td>
<td align="left">速度快，用户体验良好</td>
<td align="left">切换加载资源，速度慢，用户体验差</td>
</tr>
<tr>
<td align="left">维护成本</td>
<td align="left">相对容易</td>
<td align="left">相对复杂</td>
</tr>
</tbody></table>
<p>优点：</p>
<ul>
<li>具有桌面应用的即时性、网站的可移植性和可访问性</li>
<li>用户体验好、快，内容的改变不需要重新加载整个页面</li>
<li>良好的前后端分离，分工更明确</li>
</ul>
<p>缺点：</p>
<ul>
<li>不利于搜索引擎的抓取</li>
<li>首次渲染速度相对较慢</li>
</ul>
<h2 id="46-for-in-for-of区别"><a href="#46-for-in-for-of区别" class="headerlink" title="46. for in for of区别"></a>46. for in for of区别</h2><p>for in 适合遍历对象，不适合遍历数组，for in 会遍历到原型。</p>
<p>for..of适用遍历数/数组对象/字符串/map/set等拥有迭代器对象的集合.但是不能遍历对象,因为没有迭代器对象.与forEach()不同的是，它可以正确响应break、continue和return语句</p>
<p>for-of循环不支持普通对象，但如果你想迭代一个对象的属性，你可以用for-in循环（这也是它的本职工作）或内建的Object.keys()方法</p>
<h2 id="47-路由懒加载的原理"><a href="#47-路由懒加载的原理" class="headerlink" title="47. 路由懒加载的原理"></a>47. 路由懒加载的原理</h2><p>不使用路由懒加载，请求整个appjs,所有需要的模块被webpack编译进一个js文件。</p>
<p>路由懒加载能做到性能优化的原因就是：<em><strong>webpack会将懒加载的路由分块打包到一个单独的js中去，只有加载该路由的时候，才会加载这个chunk文件。</strong></em></p>
<h2 id="48-webpack"><a href="#48-webpack" class="headerlink" title="48. webpack"></a>48. webpack</h2><h3 id="webpack是什么，能干什么？"><a href="#webpack是什么，能干什么？" class="headerlink" title="webpack是什么，能干什么？"></a>webpack是什么，能干什么？</h3><p><code>webpack</code> 是一个用于现代<code>JavaScript</code>应用程序的静态模块打包工具</p>
<p>功能：</p>
<p>1.编译代码：es6->es5,ts->js,less.scss->css</p>
<p>2.万物皆可模块化</p>
<p><img src="/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220225180727960.png" alt="image-20220225180727960"></p>
<h3 id="webpack运行流程？"><a href="#webpack运行流程？" class="headerlink" title="webpack运行流程？"></a>webpack运行流程？</h3><p>是一个串行的流程</p>
<p>当 <code>webpack</code>处理应用程序时，它会在内部构建一个依赖图，此依赖图对应映射到项目所需的每个模块（不再局限<code>js</code>文件），并生成一个或多个 <code>bundle</code></p>
<p>1.初始化</p>
<p>读取并合并<strong>配置文件</strong>和shell的参数，初始化<strong>插件</strong>运行所需要的<strong>环境</strong>参数</p>
<p>2.编译构建</p>
<p>从 Entry 发出，针对每个 Module <strong>串行调用</strong>对应的 <strong>Loader</strong> 去翻译文件内容，再找到该 Module 依赖的 Module，<strong>递归</strong>地进行编译处理</p>
<p>3.输出</p>
<p>对编译后的 Module 组合成 Chunk，把 Chunk 转换成文件，输出到文件系统</p>
<img src="%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220225181353394.png" alt="image-20220225181353394" style="zoom: 33%;" />

<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">webpack<span class="string">` 将 `</span>webpack.<span class="property">config</span>.<span class="property">js</span><span class="string">` 中的各个配置项拷贝到 `</span>options<span class="string">` 对象中，并加载用户配置的 `</span>plugins</span><br><span class="line">初始化<span class="title class_">Compiler</span>编译对象，该对象掌控者webpack声明周期，不执行具体的任务，只是进行一些调度工作</span><br></pre></td></tr></table></figure>

<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">根据配置中的 entry 找出所有的入口文件</span><br><span class="line">初始化完成后会调用Compiler的run来真正启动webpack编译构建流程</span><br><span class="line">执行了run方法后，</span><br><span class="line">首先会触发compile:，主要是构建一个Compilation对象该对象是编译阶段的主要执行者，主要会依次下述流程：执行模块创建、依赖收集、分块、打包等主要任务的对象</span><br><span class="line">build module 完成模块编译:主要调用配置的loaders，将我们的模块转成标准的JS模块</span><br></pre></td></tr></table></figure>

<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">seal 输出资源</span><br><span class="line">seal方法主要是要生成chunks，对chunks进行一系列的优化操作，并生成要输出的代码</span><br><span class="line">webpack 中的 chunk ，可以理解为配置在 entry 中的模块，或者是动态引入的模块</span><br><span class="line">根据入口和模块之间的依赖关系，组装成一个个包含多个模块的 Chunk，再把每个 Chunk 转换成一个单独的文件加入到输出列表</span><br></pre></td></tr></table></figure>

<p><img src="/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/image-20220225182216710.png" alt="image-20220225182216710"></p>
<p><code>css</code>、<code>sass</code>、<code>png</code>等这些类型的文件的时候，<code>webpack</code>则无能为力，这时候就需要配置对应的<code>loader</code>进行文件内容的解析</p>
<ul>
<li>style-loader: 将css添加到DOM的内联样式标签style里</li>
<li>css-loader :允许将css文件通过require的方式引入，并返回css代码</li>
<li>less-loader: 处理less</li>
</ul>
<p>loader 是文件加载器，能够加载资源文件，并对这些文件进行一些处理，诸如编译、压缩等，最终一起打包到指定的文件中</p>
<p>plugin 赋予了 webpack 各种灵活的功能，例如打包优化、资源管理、环境变量注入等，目的是解决 loader 无法实现的其他事</p>
<h2 id="49-proxy和object-defineProperty对比"><a href="#49-proxy和object-defineProperty对比" class="headerlink" title="49. proxy和object.defineProperty对比"></a>49. proxy和object.defineProperty对比</h2><h3 id="proxy-1"><a href="#proxy-1" class="headerlink" title="proxy"></a>proxy</h3><p><code>Proxy</code>是 <code>Object.defineProperty</code>的加强版。</p>
<p>Proxy在目标对象之前架设一层“拦截”，外界对该对象的访问，都必须先通过这层拦截，可以对外界的访问进行过滤和改写。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> proxy = <span class="keyword">new</span> <span class="title class_">Proxy</span>(target, handler);</span><br></pre></td></tr></table></figure>

<ul>
<li><strong><code>Object.defineProperty</code>只能劫持对象的属性，对新增属性需要手动进行 Observe，而 <code>Proxy</code> 是直接代理对象。</strong></li>
<li><strong><code>Proxy</code>可以直接监听数组的变化</strong></li>
<li>Proxy 作为新标准，从长远来看，JS 引擎会继续优化 ，但是<strong>兼容性差</strong></li>
</ul>
</script></li></ul></blockquote>
      

      
        <div class="page-reward">
          <a href="javascript:;" class="page-reward-btn tooltip-top">
            <div class="tooltip tooltip-east">
            <span class="tooltip-item">
              赏
            </span>
            <span class="tooltip-content">
              <span class="tooltip-text">
                <span class="tooltip-inner">
                  <p class="reward-p"><i class="icon icon-quo-left"></i>谢谢你请我吃糖果<i class="icon icon-quo-right"></i></p>
                  <div class="reward-box">
                    
                    
                  </div>
                </span>
              </span>
            </span>
          </div>
          </a>
        </div>
      
    </div>
    <div class="article-info article-info-index">
      
      
      

      

      
        
<div class="share-btn share-icons tooltip-left">
  <div class="tooltip tooltip-east">
    <span class="tooltip-item">
      <a href="javascript:;" class="share-sns share-outer">
        <i class="icon icon-share"></i>
      </a>
    </span>
    <span class="tooltip-content">
      <div class="share-wrap">
        <div class="share-icons">
          <a class="weibo share-sns" href="javascript:;" data-type="weibo">
            <i class="icon icon-weibo"></i>
          </a>
          <a class="weixin share-sns wxFab" href="javascript:;" data-type="weixin">
            <i class="icon icon-weixin"></i>
          </a>
          <a class="qq share-sns" href="javascript:;" data-type="qq">
            <i class="icon icon-qq"></i>
          </a>
          <a class="douban share-sns" href="javascript:;" data-type="douban">
            <i class="icon icon-douban"></i>
          </a>
          <a class="qzone share-sns" href="javascript:;" data-type="qzone">
            <i class="icon icon-qzone"></i>
          </a>
          <a class="facebook share-sns" href="javascript:;" data-type="facebook">
            <i class="icon icon-facebook"></i>
          </a>
          <a class="twitter share-sns" href="javascript:;" data-type="twitter">
            <i class="icon icon-twitter"></i>
          </a>
          <a class="google share-sns" href="javascript:;" data-type="google">
            <i class="icon icon-google"></i>
          </a>
        </div>
      </div>
    </span>
  </div>
</div>

<div class="page-modal wx-share js-wx-box">
    <a class="close js-modal-close" href="javascript:;"><i class="icon icon-close"></i></a>
    <p>扫一扫，分享到微信</p>
    <div class="wx-qrcode">
      <img src="//pan.baidu.com/share/qrcode?url=http://bzayyz.gitee.io/2022/02/16/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95/" alt="微信分享二维码">
    </div>
</div>

<div class="mask js-mask"></div>
      
      <div class="clearfix"></div>
    </div>
  </div>
</article>

  
<nav id="article-nav">
  
    <a href="/2022/02/20/%E8%A7%A3%E5%86%B3%E5%8A%A8%E6%80%81%E8%B7%AF%E7%94%B1%E5%88%B7%E6%96%B0%E9%97%AE%E9%A2%98/" id="article-nav-newer" class="article-nav-link-wrap">
      <i class="icon-circle-left"></i>
      <div class="article-nav-title">
        
          解决动态路由刷新空白和404问题
        
      </div>
    </a>
  
  
    <a href="/2022/02/13/%E5%89%8D%E7%AB%AF%E7%AE%97%E6%B3%95/" id="article-nav-older" class="article-nav-link-wrap">
      <div class="article-nav-title">前端算法学习</div>
      <i class="icon-circle-right"></i>
    </a>
  
</nav>


<aside class="wrap-side-operation">
    <div class="mod-side-operation">
        
        <div class="jump-container" id="js-jump-container" style="display:none;">
            <a href="javascript:void(0)" class="mod-side-operation__jump-to-top">
                <i class="icon-font icon-back"></i>
            </a>
            <div id="js-jump-plan-container" class="jump-plan-container" style="top: -11px;">
                <i class="icon-font icon-plane jump-plane"></i>
            </div>
        </div>
        
        
        <div class="toc-container tooltip-left">
            <i class="icon-font icon-category"></i>
            <div class="tooltip tooltip-east">
                <span class="tooltip-item">
                </span>
                <span class="tooltip-content">
                    <div class="toc-article">
                    <ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#1-http-https"><span class="toc-number">1.</span> <span class="toc-text">1.http,https</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#1-1-%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5"><span class="toc-number">1.0.1.</span> <span class="toc-text">1.1 基本概念:</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#1-2-%E5%8C%BA%E5%88%AB"><span class="toc-number">1.0.2.</span> <span class="toc-text">1.2 区别</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#1-3-https%E5%B7%A5%E4%BD%9C%E8%BF%87%E7%A8%8B"><span class="toc-number">1.0.3.</span> <span class="toc-text">1.3 https工作过程</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#1-4-https%E4%BC%98%E7%BC%BA%E7%82%B9"><span class="toc-number">1.0.4.</span> <span class="toc-text">1.4 https优缺点</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#1-5-SSL%E5%8D%8F%E8%AE%AE%E4%B9%8B%E6%95%B0%E6%8D%AE%E5%8A%A0%E5%AF%86%E8%BF%87%E7%A8%8B"><span class="toc-number">1.0.5.</span> <span class="toc-text">1.5 SSL协议之数据加密过程</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#1-6-HTTP2-0-%E7%9A%84%E7%89%B9%E6%80%A7"><span class="toc-number">1.0.6.</span> <span class="toc-text">1.6 HTTP2.0 的特性</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#1-7-HTTP%E6%BC%94%E5%8C%96%E8%BF%87%E7%A8%8B"><span class="toc-number">1.0.7.</span> <span class="toc-text">1.7 HTTP演化过程</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#1-8-http%E8%AF%B7%E6%B1%82%E5%A4%B4%E6%9C%89%E5%93%AA%E4%BA%9B%EF%BC%9F"><span class="toc-number">1.0.8.</span> <span class="toc-text">1.8  http请求头有哪些？</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#2-TCP-UDP"><span class="toc-number">2.</span> <span class="toc-text">2.TCP UDP</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#3-websocket"><span class="toc-number">3.</span> <span class="toc-text">3.websocket</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#4-%E4%B8%80%E4%B8%AA%E5%9B%BE%E7%89%87url%E8%AE%BF%E9%97%AE%E5%90%8E%E7%9B%B4%E6%8E%A5%E4%B8%8B%E8%BD%BD%E6%80%8E%E4%B9%88%E5%AE%9E%E7%8E%B0%EF%BC%9F"><span class="toc-number">4.</span> <span class="toc-text">4.  一个图片url访问后直接下载怎么实现？</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#5-Bom%E5%AF%B9%E8%B1%A1%E6%96%B9%E6%B3%95"><span class="toc-number">5.</span> <span class="toc-text">5. Bom对象方法</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#6-%E6%B5%8F%E8%A7%88%E5%99%A8%E7%8A%B6%E6%80%81%E7%A0%81"><span class="toc-number">6.</span> <span class="toc-text">6. 浏览器状态码</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#7-fetch%E5%8F%91%E4%B8%A4%E6%AC%A1%E8%AF%B7%E6%B1%82%E7%9A%84%E5%8E%9F%E5%9B%A0"><span class="toc-number">7.</span> <span class="toc-text">7.fetch发两次请求的原因</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#8-cookie-session-sessionStorage-localStorage"><span class="toc-number">8.</span> <span class="toc-text">8. cookie session sessionStorage localStorage</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#cookie-session"><span class="toc-number">8.0.0.1.</span> <span class="toc-text">cookie session</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#sessionStorage-localStorage"><span class="toc-number">8.0.0.2.</span> <span class="toc-text">sessionStorage localStorage</span></a></li></ol></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#9-web-worker"><span class="toc-number">9.</span> <span class="toc-text">9. web worker()</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#10-CDN-%E5%B8%A6%E6%9D%A5%E7%9A%84%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96"><span class="toc-number">10.</span> <span class="toc-text">10. CDN 带来的性能优化**</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#CDN%E7%9A%84%E5%9F%BA%E6%9C%AC%E5%B7%A5%E4%BD%9C%E8%BF%87%E7%A8%8B"><span class="toc-number">10.0.0.1.</span> <span class="toc-text">CDN的基本工作过程</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#CDN%E7%9A%84%E4%BC%98%E5%8A%BF"><span class="toc-number">10.0.0.2.</span> <span class="toc-text">CDN的优势</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#CDN-amp-%E9%9D%99%E6%80%81%E8%B5%84%E6%BA%90"><span class="toc-number">10.0.0.3.</span> <span class="toc-text">CDN &amp; 静态资源</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#CDN-amp-Cookie"><span class="toc-number">10.0.0.4.</span> <span class="toc-text">CDN &amp; Cookie</span></a></li></ol></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#11-JS%E5%BA%95%E5%B1%82"><span class="toc-number">11.</span> <span class="toc-text">11.JS底层</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#12-%E6%95%B0%E6%8D%AE%E5%8F%AF%E8%A7%86%E5%8C%96"><span class="toc-number">12.</span> <span class="toc-text">12. 数据可视化</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#13-http%E5%A4%B4%E9%83%A8%E7%9A%84%E5%B8%B8%E8%A7%81%E5%AD%97%E6%AE%B5"><span class="toc-number">13.</span> <span class="toc-text">13. http头部的常见字段</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#14-HTTP%E6%94%AF%E6%8C%81%E7%9A%84%E6%96%B9%E6%B3%95"><span class="toc-number">14.</span> <span class="toc-text">14. HTTP支持的方法</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#15-HTML5"><span class="toc-number">15.</span> <span class="toc-text">15. HTML5</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#16-%E5%9C%A8%E5%9C%B0%E5%9D%80%E6%A0%8F%E9%87%8C%E8%BE%93%E5%85%A5%E4%B8%80%E4%B8%AAURL-%E5%88%B0%E8%BF%99%E4%B8%AA%E9%A1%B5%E9%9D%A2%E5%91%88%E7%8E%B0%E5%87%BA%E6%9D%A5%EF%BC%8C%E4%B8%AD%E9%97%B4%E4%BC%9A%E5%8F%91%E7%94%9F%E4%BB%80%E4%B9%88%EF%BC%9F"><span class="toc-number">16.</span> <span class="toc-text">16. 在地址栏里输入一个URL,到这个页面呈现出来，中间会发生什么？</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#17-%E7%94%BB%E4%B8%80%E6%9D%A10-5px%E7%9A%84%E7%BA%BF"><span class="toc-number">17.</span> <span class="toc-text">17. 画一条0.5px的线</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#18-Flex%E4%B8%8E%E4%B8%A4%E6%A0%8F%E5%B8%83%E5%B1%80%E3%80%81%E4%B8%89%E6%A0%8F%E5%B8%83%E5%B1%80"><span class="toc-number">18.</span> <span class="toc-text">18.Flex与两栏布局、三栏布局</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#Flex%E4%B8%8E%E4%B8%A4%E6%A0%8F%E5%B8%83%E5%B1%80%E3%80%81%E4%B8%89%E6%A0%8F%E5%B8%83%E5%B1%80-%E6%8E%98%E9%87%91-juejin-cn"><span class="toc-number">18.0.1.</span> <span class="toc-text">Flex与两栏布局、三栏布局 - 掘金 (juejin.cn)</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%B5%AE%E5%8A%A8-28%E6%9D%A1%E6%B6%88%E6%81%AF-%E5%9B%BE%E6%96%87%E8%AF%A6%E7%BB%86-%E6%9C%80%E9%80%9A%E4%BF%97%E6%98%93%E6%87%82%E7%9A%84CSS-%E6%B5%AE%E5%8A%A8float%E5%B1%9E%E6%80%A7%E8%AF%A6%E8%A7%A3-Sunny%E7%8E%8B%E7%BB%B4-CSDN%E5%8D%9A%E5%AE%A2-css-float"><span class="toc-number">18.0.2.</span> <span class="toc-text">浮动(28条消息) (图文详细)最通俗易懂的CSS 浮动float属性详解_Sunny王维-CSDN博客_css float</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#30-%E5%88%86%E9%92%9F%E5%AD%A6%E4%BC%9A-Flex-%E5%B8%83%E5%B1%80-%E7%9F%A5%E4%B9%8E-zhihu-com"><span class="toc-number">18.0.3.</span> <span class="toc-text">30 分钟学会 Flex 布局 - 知乎 (zhihu.com)</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#19-BFC"><span class="toc-number">19.</span> <span class="toc-text">19. BFC</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#1-BFC-%E5%AE%9A%E4%B9%89"><span class="toc-number">19.0.0.1.</span> <span class="toc-text">1 BFC 定义</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#2-BFC%E7%9A%84%E7%94%9F%E6%88%90"><span class="toc-number">19.0.0.2.</span> <span class="toc-text">2.BFC的生成</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#3-BFC%E7%9A%84%E7%BA%A6%E6%9D%9F%E8%A7%84%E5%88%99"><span class="toc-number">19.0.0.3.</span> <span class="toc-text">3 BFC的约束规则</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#4-BFC%E5%9C%A8%E5%B8%83%E5%B1%80%E4%B8%AD%E7%9A%84%E5%BA%94%E7%94%A8"><span class="toc-number">19.0.0.4.</span> <span class="toc-text">4. BFC在布局中的应用</span></a><ol class="toc-child"><li class="toc-item toc-level-6"><a class="toc-link" href="#4-1-%E9%98%B2%E6%AD%A2margin%E9%87%8D%E5%8F%A0%EF%BC%88%E5%A1%8C%E9%99%B7%EF%BC%89"><span class="toc-number">19.0.0.4.1.</span> <span class="toc-text">4.1 　防止margin重叠（塌陷）</span></a></li><li class="toc-item toc-level-6"><a class="toc-link" href="#4-2-%E6%B8%85%E9%99%A4%E5%86%85%E9%83%A8%E6%B5%AE%E5%8A%A8"><span class="toc-number">19.0.0.4.2.</span> <span class="toc-text">4.2 清除内部浮动</span></a></li><li class="toc-item toc-level-6"><a class="toc-link" href="#4-3-%E8%87%AA%E9%80%82%E5%BA%94%E5%A4%9A%E6%A0%8F%E5%B8%83%E5%B1%80%E7%9A%84"><span class="toc-number">19.0.0.4.3.</span> <span class="toc-text">4.3 自适应多栏布局的</span></a></li></ol></li></ol></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#20-%E5%9E%82%E7%9B%B4%E5%B1%85%E4%B8%AD%E7%9A%84%E6%96%B9%E6%B3%95"><span class="toc-number">20.</span> <span class="toc-text">20. 垂直居中的方法</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%B0%B4%E5%B9%B3%E5%B1%85%E4%B8%AD%EF%BC%9A"><span class="toc-number">20.1.</span> <span class="toc-text">水平居中：</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#21-visibility-x3D-hidden-opacity-x3D-0%EF%BC%8Cdisplay-none"><span class="toc-number">21.</span> <span class="toc-text">21. visibility&#x3D;hidden, opacity&#x3D;0，display:none</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#22-position%E5%B1%9E%E6%80%A7-%E6%AF%94%E8%BE%83"><span class="toc-number">22.</span> <span class="toc-text">22. position属性 比较</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#23-%E6%B5%8F%E8%A7%88%E5%99%A8%E4%B8%BB%E8%A6%81%E7%BB%84%E6%88%90%E4%B8%8E%E6%B5%8F%E8%A7%88%E5%99%A8%E7%BA%BF%E7%A8%8B"><span class="toc-number">23.</span> <span class="toc-text">23. 浏览器主要组成与浏览器线程</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#1-%E6%B5%8F%E8%A7%88%E5%99%A8%E5%9F%BA%E6%9C%AC%E7%BB%84%E6%88%90"><span class="toc-number">23.1.</span> <span class="toc-text">1.浏览器基本组成</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-%E6%B5%8F%E8%A7%88%E5%99%A8%E4%B8%AD%E7%9A%84%E8%BF%9B%E7%A8%8B%E4%B8%8E%E7%BA%BF%E7%A8%8B"><span class="toc-number">23.2.</span> <span class="toc-text">2.浏览器中的进程与线程</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3-%E6%B5%8F%E8%A7%88%E5%99%A8%E6%B8%B2%E6%9F%93%E7%9A%84%E8%BF%9B%E7%A8%8B%E8%A7%86%E8%A7%92"><span class="toc-number">23.3.</span> <span class="toc-text">3.浏览器渲染的进程视角</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%A0%87%E7%AD%BE%E9%A1%B5%E4%B8%8E%E8%BF%9B%E7%A8%8B%E7%9A%84%E5%85%B3%E7%B3%BB"><span class="toc-number">23.3.1.</span> <span class="toc-text">标签页与进程的关系</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4-%E6%B5%8F%E8%A7%88%E5%99%A8%E6%B8%B2%E6%9F%93%E7%9A%84%E7%BA%BF%E7%A8%8B%E8%A7%86%E8%A7%92%EF%BC%9A"><span class="toc-number">23.4.</span> <span class="toc-text">4.浏览器渲染的线程视角：</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#%E8%AF%A6%E7%BB%86%E6%B5%81%E7%A8%8B"><span class="toc-number">23.4.0.1.</span> <span class="toc-text">详细流程</span></a></li></ol></li></ol></li></ol></li></ol>
                    </div>
                </span>
            </div>
        </div>
        
    </div>
</aside>



  
  
  

  

  

  


          </div>
        </div>
      </div>
      <footer id="footer">
  <div class="outer">
    <div id="footer-info">
    	<div class="footer-left">
    		&copy; 2022 John Doe
    	</div>
      	<div class="footer-right">
      		<a href="http://hexo.io/" target="_blank">Hexo</a>  Theme <a href="https://github.com/litten/hexo-theme-yilia" target="_blank">Yilia</a> by Litten
      	</div>
    </div>
  </div>
</footer>
    </div>
    <script>
	var yiliaConfig = {
		mathjax: false,
		isHome: false,
		isPost: true,
		isArchive: false,
		isTag: false,
		isCategory: false,
		open_in_new: false,
		toc_hide_index: true,
		root: "/",
		innerArchive: true,
		showTags: false
	}
</script>

<script>!function(t){function n(e){if(r[e])return r[e].exports;var i=r[e]={exports:{},id:e,loaded:!1};return t[e].call(i.exports,i,i.exports,n),i.loaded=!0,i.exports}var r={};n.m=t,n.c=r,n.p="./",n(0)}([function(t,n,r){r(195),t.exports=r(191)},function(t,n,r){var e=r(3),i=r(52),o=r(27),u=r(28),c=r(53),f="prototype",a=function(t,n,r){var s,l,h,v,p=t&a.F,d=t&a.G,y=t&a.S,g=t&a.P,b=t&a.B,m=d?e:y?e[n]||(e[n]={}):(e[n]||{})[f],x=d?i:i[n]||(i[n]={}),w=x[f]||(x[f]={});d&&(r=n);for(s in r)l=!p&&m&&void 0!==m[s],h=(l?m:r)[s],v=b&&l?c(h,e):g&&"function"==typeof h?c(Function.call,h):h,m&&u(m,s,h,t&a.U),x[s]!=h&&o(x,s,v),g&&w[s]!=h&&(w[s]=h)};e.core=i,a.F=1,a.G=2,a.S=4,a.P=8,a.B=16,a.W=32,a.U=64,a.R=128,t.exports=a},function(t,n,r){var e=r(6);t.exports=function(t){if(!e(t))throw TypeError(t+" is not an object!");return t}},function(t,n){var r=t.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=r)},function(t,n){t.exports=function(t){try{return!!t()}catch(t){return!0}}},function(t,n){var r=t.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=r)},function(t,n){t.exports=function(t){return"object"==typeof t?null!==t:"function"==typeof t}},function(t,n,r){var e=r(126)("wks"),i=r(76),o=r(3).Symbol,u="function"==typeof o;(t.exports=function(t){return e[t]||(e[t]=u&&o[t]||(u?o:i)("Symbol."+t))}).store=e},function(t,n){var r={}.hasOwnProperty;t.exports=function(t,n){return r.call(t,n)}},function(t,n,r){var e=r(94),i=r(33);t.exports=function(t){return e(i(t))}},function(t,n,r){t.exports=!r(4)(function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})},function(t,n,r){var e=r(2),i=r(167),o=r(50),u=Object.defineProperty;n.f=r(10)?Object.defineProperty:function(t,n,r){if(e(t),n=o(n,!0),e(r),i)try{return u(t,n,r)}catch(t){}if("get"in r||"set"in r)throw TypeError("Accessors not supported!");return"value"in r&&(t[n]=r.value),t}},function(t,n,r){t.exports=!r(18)(function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})},function(t,n,r){var e=r(14),i=r(22);t.exports=r(12)?function(t,n,r){return e.f(t,n,i(1,r))}:function(t,n,r){return t[n]=r,t}},function(t,n,r){var e=r(20),i=r(58),o=r(42),u=Object.defineProperty;n.f=r(12)?Object.defineProperty:function(t,n,r){if(e(t),n=o(n,!0),e(r),i)try{return u(t,n,r)}catch(t){}if("get"in r||"set"in r)throw TypeError("Accessors not supported!");return"value"in r&&(t[n]=r.value),t}},function(t,n,r){var e=r(40)("wks"),i=r(23),o=r(5).Symbol,u="function"==typeof o;(t.exports=function(t){return e[t]||(e[t]=u&&o[t]||(u?o:i)("Symbol."+t))}).store=e},function(t,n,r){var e=r(67),i=Math.min;t.exports=function(t){return t>0?i(e(t),9007199254740991):0}},function(t,n,r){var e=r(46);t.exports=function(t){return Object(e(t))}},function(t,n){t.exports=function(t){try{return!!t()}catch(t){return!0}}},function(t,n,r){var e=r(63),i=r(34);t.exports=Object.keys||function(t){return e(t,i)}},function(t,n,r){var e=r(21);t.exports=function(t){if(!e(t))throw TypeError(t+" is not an object!");return t}},function(t,n){t.exports=function(t){return"object"==typeof t?null!==t:"function"==typeof t}},function(t,n){t.exports=function(t,n){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:n}}},function(t,n){var r=0,e=Math.random();t.exports=function(t){return"Symbol(".concat(void 0===t?"":t,")_",(++r+e).toString(36))}},function(t,n){var r={}.hasOwnProperty;t.exports=function(t,n){return r.call(t,n)}},function(t,n){var r=t.exports={version:"2.4.0"};"number"==typeof __e&&(__e=r)},function(t,n){t.exports=function(t){if("function"!=typeof t)throw TypeError(t+" is not a function!");return t}},function(t,n,r){var e=r(11),i=r(66);t.exports=r(10)?function(t,n,r){return e.f(t,n,i(1,r))}:function(t,n,r){return t[n]=r,t}},function(t,n,r){var e=r(3),i=r(27),o=r(24),u=r(76)("src"),c="toString",f=Function[c],a=(""+f).split(c);r(52).inspectSource=function(t){return f.call(t)},(t.exports=function(t,n,r,c){var f="function"==typeof r;f&&(o(r,"name")||i(r,"name",n)),t[n]!==r&&(f&&(o(r,u)||i(r,u,t[n]?""+t[n]:a.join(String(n)))),t===e?t[n]=r:c?t[n]?t[n]=r:i(t,n,r):(delete t[n],i(t,n,r)))})(Function.prototype,c,function(){return"function"==typeof this&&this[u]||f.call(this)})},function(t,n,r){var e=r(1),i=r(4),o=r(46),u=function(t,n,r,e){var i=String(o(t)),u="<"+n;return""!==r&&(u+=" "+r+'="'+String(e).replace(/"/g,"&quot;")+'"'),u+">"+i+"</"+n+">"};t.exports=function(t,n){var r={};r[t]=n(u),e(e.P+e.F*i(function(){var n=""[t]('"');return n!==n.toLowerCase()||n.split('"').length>3}),"String",r)}},function(t,n,r){var e=r(115),i=r(46);t.exports=function(t){return e(i(t))}},function(t,n,r){var e=r(116),i=r(66),o=r(30),u=r(50),c=r(24),f=r(167),a=Object.getOwnPropertyDescriptor;n.f=r(10)?a:function(t,n){if(t=o(t),n=u(n,!0),f)try{return a(t,n)}catch(t){}if(c(t,n))return i(!e.f.call(t,n),t[n])}},function(t,n,r){var e=r(24),i=r(17),o=r(145)("IE_PROTO"),u=Object.prototype;t.exports=Object.getPrototypeOf||function(t){return t=i(t),e(t,o)?t[o]:"function"==typeof t.constructor&&t instanceof t.constructor?t.constructor.prototype:t instanceof Object?u:null}},function(t,n){t.exports=function(t){if(void 0==t)throw TypeError("Can't call method on  "+t);return t}},function(t,n){t.exports="constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf".split(",")},function(t,n){t.exports={}},function(t,n){t.exports=!0},function(t,n){n.f={}.propertyIsEnumerable},function(t,n,r){var e=r(14).f,i=r(8),o=r(15)("toStringTag");t.exports=function(t,n,r){t&&!i(t=r?t:t.prototype,o)&&e(t,o,{configurable:!0,value:n})}},function(t,n,r){var e=r(40)("keys"),i=r(23);t.exports=function(t){return e[t]||(e[t]=i(t))}},function(t,n,r){var e=r(5),i="__core-js_shared__",o=e[i]||(e[i]={});t.exports=function(t){return o[t]||(o[t]={})}},function(t,n){var r=Math.ceil,e=Math.floor;t.exports=function(t){return isNaN(t=+t)?0:(t>0?e:r)(t)}},function(t,n,r){var e=r(21);t.exports=function(t,n){if(!e(t))return t;var r,i;if(n&&"function"==typeof(r=t.toString)&&!e(i=r.call(t)))return i;if("function"==typeof(r=t.valueOf)&&!e(i=r.call(t)))return i;if(!n&&"function"==typeof(r=t.toString)&&!e(i=r.call(t)))return i;throw TypeError("Can't convert object to primitive value")}},function(t,n,r){var e=r(5),i=r(25),o=r(36),u=r(44),c=r(14).f;t.exports=function(t){var n=i.Symbol||(i.Symbol=o?{}:e.Symbol||{});"_"==t.charAt(0)||t in n||c(n,t,{value:u.f(t)})}},function(t,n,r){n.f=r(15)},function(t,n){var r={}.toString;t.exports=function(t){return r.call(t).slice(8,-1)}},function(t,n){t.exports=function(t){if(void 0==t)throw TypeError("Can't call method on  "+t);return t}},function(t,n,r){var e=r(4);t.exports=function(t,n){return!!t&&e(function(){n?t.call(null,function(){},1):t.call(null)})}},function(t,n,r){var e=r(53),i=r(115),o=r(17),u=r(16),c=r(203);t.exports=function(t,n){var r=1==t,f=2==t,a=3==t,s=4==t,l=6==t,h=5==t||l,v=n||c;return function(n,c,p){for(var d,y,g=o(n),b=i(g),m=e(c,p,3),x=u(b.length),w=0,S=r?v(n,x):f?v(n,0):void 0;x>w;w++)if((h||w in b)&&(d=b[w],y=m(d,w,g),t))if(r)S[w]=y;else if(y)switch(t){case 3:return!0;case 5:return d;case 6:return w;case 2:S.push(d)}else if(s)return!1;return l?-1:a||s?s:S}}},function(t,n,r){var e=r(1),i=r(52),o=r(4);t.exports=function(t,n){var r=(i.Object||{})[t]||Object[t],u={};u[t]=n(r),e(e.S+e.F*o(function(){r(1)}),"Object",u)}},function(t,n,r){var e=r(6);t.exports=function(t,n){if(!e(t))return t;var r,i;if(n&&"function"==typeof(r=t.toString)&&!e(i=r.call(t)))return i;if("function"==typeof(r=t.valueOf)&&!e(i=r.call(t)))return i;if(!n&&"function"==typeof(r=t.toString)&&!e(i=r.call(t)))return i;throw TypeError("Can't convert object to primitive value")}},function(t,n,r){var e=r(5),i=r(25),o=r(91),u=r(13),c="prototype",f=function(t,n,r){var a,s,l,h=t&f.F,v=t&f.G,p=t&f.S,d=t&f.P,y=t&f.B,g=t&f.W,b=v?i:i[n]||(i[n]={}),m=b[c],x=v?e:p?e[n]:(e[n]||{})[c];v&&(r=n);for(a in r)(s=!h&&x&&void 0!==x[a])&&a in b||(l=s?x[a]:r[a],b[a]=v&&"function"!=typeof x[a]?r[a]:y&&s?o(l,e):g&&x[a]==l?function(t){var n=function(n,r,e){if(this instanceof t){switch(arguments.length){case 0:return new t;case 1:return new t(n);case 2:return new t(n,r)}return new t(n,r,e)}return t.apply(this,arguments)};return n[c]=t[c],n}(l):d&&"function"==typeof l?o(Function.call,l):l,d&&((b.virtual||(b.virtual={}))[a]=l,t&f.R&&m&&!m[a]&&u(m,a,l)))};f.F=1,f.G=2,f.S=4,f.P=8,f.B=16,f.W=32,f.U=64,f.R=128,t.exports=f},function(t,n){var r=t.exports={version:"2.4.0"};"number"==typeof __e&&(__e=r)},function(t,n,r){var e=r(26);t.exports=function(t,n,r){if(e(t),void 0===n)return t;switch(r){case 1:return function(r){return t.call(n,r)};case 2:return function(r,e){return t.call(n,r,e)};case 3:return function(r,e,i){return t.call(n,r,e,i)}}return function(){return t.apply(n,arguments)}}},function(t,n,r){var e=r(183),i=r(1),o=r(126)("metadata"),u=o.store||(o.store=new(r(186))),c=function(t,n,r){var i=u.get(t);if(!i){if(!r)return;u.set(t,i=new e)}var o=i.get(n);if(!o){if(!r)return;i.set(n,o=new e)}return o},f=function(t,n,r){var e=c(n,r,!1);return void 0!==e&&e.has(t)},a=function(t,n,r){var e=c(n,r,!1);return void 0===e?void 0:e.get(t)},s=function(t,n,r,e){c(r,e,!0).set(t,n)},l=function(t,n){var r=c(t,n,!1),e=[];return r&&r.forEach(function(t,n){e.push(n)}),e},h=function(t){return void 0===t||"symbol"==typeof t?t:String(t)},v=function(t){i(i.S,"Reflect",t)};t.exports={store:u,map:c,has:f,get:a,set:s,keys:l,key:h,exp:v}},function(t,n,r){"use strict";if(r(10)){var e=r(69),i=r(3),o=r(4),u=r(1),c=r(127),f=r(152),a=r(53),s=r(68),l=r(66),h=r(27),v=r(73),p=r(67),d=r(16),y=r(75),g=r(50),b=r(24),m=r(180),x=r(114),w=r(6),S=r(17),_=r(137),O=r(70),E=r(32),P=r(71).f,j=r(154),F=r(76),M=r(7),A=r(48),N=r(117),T=r(146),I=r(155),k=r(80),L=r(123),R=r(74),C=r(130),D=r(160),U=r(11),W=r(31),G=U.f,B=W.f,V=i.RangeError,z=i.TypeError,q=i.Uint8Array,K="ArrayBuffer",J="Shared"+K,Y="BYTES_PER_ELEMENT",H="prototype",$=Array[H],X=f.ArrayBuffer,Q=f.DataView,Z=A(0),tt=A(2),nt=A(3),rt=A(4),et=A(5),it=A(6),ot=N(!0),ut=N(!1),ct=I.values,ft=I.keys,at=I.entries,st=$.lastIndexOf,lt=$.reduce,ht=$.reduceRight,vt=$.join,pt=$.sort,dt=$.slice,yt=$.toString,gt=$.toLocaleString,bt=M("iterator"),mt=M("toStringTag"),xt=F("typed_constructor"),wt=F("def_constructor"),St=c.CONSTR,_t=c.TYPED,Ot=c.VIEW,Et="Wrong length!",Pt=A(1,function(t,n){return Tt(T(t,t[wt]),n)}),jt=o(function(){return 1===new q(new Uint16Array([1]).buffer)[0]}),Ft=!!q&&!!q[H].set&&o(function(){new q(1).set({})}),Mt=function(t,n){if(void 0===t)throw z(Et);var r=+t,e=d(t);if(n&&!m(r,e))throw V(Et);return e},At=function(t,n){var r=p(t);if(r<0||r%n)throw V("Wrong offset!");return r},Nt=function(t){if(w(t)&&_t in t)return t;throw z(t+" is not a typed array!")},Tt=function(t,n){if(!(w(t)&&xt in t))throw z("It is not a typed array constructor!");return new t(n)},It=function(t,n){return kt(T(t,t[wt]),n)},kt=function(t,n){for(var r=0,e=n.length,i=Tt(t,e);e>r;)i[r]=n[r++];return i},Lt=function(t,n,r){G(t,n,{get:function(){return this._d[r]}})},Rt=function(t){var n,r,e,i,o,u,c=S(t),f=arguments.length,s=f>1?arguments[1]:void 0,l=void 0!==s,h=j(c);if(void 0!=h&&!_(h)){for(u=h.call(c),e=[],n=0;!(o=u.next()).done;n++)e.push(o.value);c=e}for(l&&f>2&&(s=a(s,arguments[2],2)),n=0,r=d(c.length),i=Tt(this,r);r>n;n++)i[n]=l?s(c[n],n):c[n];return i},Ct=function(){for(var t=0,n=arguments.length,r=Tt(this,n);n>t;)r[t]=arguments[t++];return r},Dt=!!q&&o(function(){gt.call(new q(1))}),Ut=function(){return gt.apply(Dt?dt.call(Nt(this)):Nt(this),arguments)},Wt={copyWithin:function(t,n){return D.call(Nt(this),t,n,arguments.length>2?arguments[2]:void 0)},every:function(t){return rt(Nt(this),t,arguments.length>1?arguments[1]:void 0)},fill:function(t){return C.apply(Nt(this),arguments)},filter:function(t){return It(this,tt(Nt(this),t,arguments.length>1?arguments[1]:void 0))},find:function(t){return et(Nt(this),t,arguments.length>1?arguments[1]:void 0)},findIndex:function(t){return it(Nt(this),t,arguments.length>1?arguments[1]:void 0)},forEach:function(t){Z(Nt(this),t,arguments.length>1?arguments[1]:void 0)},indexOf:function(t){return ut(Nt(this),t,arguments.length>1?arguments[1]:void 0)},includes:function(t){return ot(Nt(this),t,arguments.length>1?arguments[1]:void 0)},join:function(t){return vt.apply(Nt(this),arguments)},lastIndexOf:function(t){return st.apply(Nt(this),arguments)},map:function(t){return Pt(Nt(this),t,arguments.length>1?arguments[1]:void 0)},reduce:function(t){return lt.apply(Nt(this),arguments)},reduceRight:function(t){return ht.apply(Nt(this),arguments)},reverse:function(){for(var t,n=this,r=Nt(n).length,e=Math.floor(r/2),i=0;i<e;)t=n[i],n[i++]=n[--r],n[r]=t;return n},some:function(t){return nt(Nt(this),t,arguments.length>1?arguments[1]:void 0)},sort:function(t){return pt.call(Nt(this),t)},subarray:function(t,n){var r=Nt(this),e=r.length,i=y(t,e);return new(T(r,r[wt]))(r.buffer,r.byteOffset+i*r.BYTES_PER_ELEMENT,d((void 0===n?e:y(n,e))-i))}},Gt=function(t,n){return It(this,dt.call(Nt(this),t,n))},Bt=function(t){Nt(this);var n=At(arguments[1],1),r=this.length,e=S(t),i=d(e.length),o=0;if(i+n>r)throw V(Et);for(;o<i;)this[n+o]=e[o++]},Vt={entries:function(){return at.call(Nt(this))},keys:function(){return ft.call(Nt(this))},values:function(){return ct.call(Nt(this))}},zt=function(t,n){return w(t)&&t[_t]&&"symbol"!=typeof n&&n in t&&String(+n)==String(n)},qt=function(t,n){return zt(t,n=g(n,!0))?l(2,t[n]):B(t,n)},Kt=function(t,n,r){return!(zt(t,n=g(n,!0))&&w(r)&&b(r,"value"))||b(r,"get")||b(r,"set")||r.configurable||b(r,"writable")&&!r.writable||b(r,"enumerable")&&!r.enumerable?G(t,n,r):(t[n]=r.value,t)};St||(W.f=qt,U.f=Kt),u(u.S+u.F*!St,"Object",{getOwnPropertyDescriptor:qt,defineProperty:Kt}),o(function(){yt.call({})})&&(yt=gt=function(){return vt.call(this)});var Jt=v({},Wt);v(Jt,Vt),h(Jt,bt,Vt.values),v(Jt,{slice:Gt,set:Bt,constructor:function(){},toString:yt,toLocaleString:Ut}),Lt(Jt,"buffer","b"),Lt(Jt,"byteOffset","o"),Lt(Jt,"byteLength","l"),Lt(Jt,"length","e"),G(Jt,mt,{get:function(){return this[_t]}}),t.exports=function(t,n,r,f){f=!!f;var a=t+(f?"Clamped":"")+"Array",l="Uint8Array"!=a,v="get"+t,p="set"+t,y=i[a],g=y||{},b=y&&E(y),m=!y||!c.ABV,S={},_=y&&y[H],j=function(t,r){var e=t._d;return e.v[v](r*n+e.o,jt)},F=function(t,r,e){var i=t._d;f&&(e=(e=Math.round(e))<0?0:e>255?255:255&e),i.v[p](r*n+i.o,e,jt)},M=function(t,n){G(t,n,{get:function(){return j(this,n)},set:function(t){return F(this,n,t)},enumerable:!0})};m?(y=r(function(t,r,e,i){s(t,y,a,"_d");var o,u,c,f,l=0,v=0;if(w(r)){if(!(r instanceof X||(f=x(r))==K||f==J))return _t in r?kt(y,r):Rt.call(y,r);o=r,v=At(e,n);var p=r.byteLength;if(void 0===i){if(p%n)throw V(Et);if((u=p-v)<0)throw V(Et)}else if((u=d(i)*n)+v>p)throw V(Et);c=u/n}else c=Mt(r,!0),u=c*n,o=new X(u);for(h(t,"_d",{b:o,o:v,l:u,e:c,v:new Q(o)});l<c;)M(t,l++)}),_=y[H]=O(Jt),h(_,"constructor",y)):L(function(t){new y(null),new y(t)},!0)||(y=r(function(t,r,e,i){s(t,y,a);var o;return w(r)?r instanceof X||(o=x(r))==K||o==J?void 0!==i?new g(r,At(e,n),i):void 0!==e?new g(r,At(e,n)):new g(r):_t in r?kt(y,r):Rt.call(y,r):new g(Mt(r,l))}),Z(b!==Function.prototype?P(g).concat(P(b)):P(g),function(t){t in y||h(y,t,g[t])}),y[H]=_,e||(_.constructor=y));var A=_[bt],N=!!A&&("values"==A.name||void 0==A.name),T=Vt.values;h(y,xt,!0),h(_,_t,a),h(_,Ot,!0),h(_,wt,y),(f?new y(1)[mt]==a:mt in _)||G(_,mt,{get:function(){return a}}),S[a]=y,u(u.G+u.W+u.F*(y!=g),S),u(u.S,a,{BYTES_PER_ELEMENT:n,from:Rt,of:Ct}),Y in _||h(_,Y,n),u(u.P,a,Wt),R(a),u(u.P+u.F*Ft,a,{set:Bt}),u(u.P+u.F*!N,a,Vt),u(u.P+u.F*(_.toString!=yt),a,{toString:yt}),u(u.P+u.F*o(function(){new y(1).slice()}),a,{slice:Gt}),u(u.P+u.F*(o(function(){return[1,2].toLocaleString()!=new y([1,2]).toLocaleString()})||!o(function(){_.toLocaleString.call([1,2])})),a,{toLocaleString:Ut}),k[a]=N?A:T,e||N||h(_,bt,T)}}else t.exports=function(){}},function(t,n){var r={}.toString;t.exports=function(t){return r.call(t).slice(8,-1)}},function(t,n,r){var e=r(21),i=r(5).document,o=e(i)&&e(i.createElement);t.exports=function(t){return o?i.createElement(t):{}}},function(t,n,r){t.exports=!r(12)&&!r(18)(function(){return 7!=Object.defineProperty(r(57)("div"),"a",{get:function(){return 7}}).a})},function(t,n,r){"use strict";var e=r(36),i=r(51),o=r(64),u=r(13),c=r(8),f=r(35),a=r(96),s=r(38),l=r(103),h=r(15)("iterator"),v=!([].keys&&"next"in[].keys()),p="keys",d="values",y=function(){return this};t.exports=function(t,n,r,g,b,m,x){a(r,n,g);var w,S,_,O=function(t){if(!v&&t in F)return F[t];switch(t){case p:case d:return function(){return new r(this,t)}}return function(){return new r(this,t)}},E=n+" Iterator",P=b==d,j=!1,F=t.prototype,M=F[h]||F["@@iterator"]||b&&F[b],A=M||O(b),N=b?P?O("entries"):A:void 0,T="Array"==n?F.entries||M:M;if(T&&(_=l(T.call(new t)))!==Object.prototype&&(s(_,E,!0),e||c(_,h)||u(_,h,y)),P&&M&&M.name!==d&&(j=!0,A=function(){return M.call(this)}),e&&!x||!v&&!j&&F[h]||u(F,h,A),f[n]=A,f[E]=y,b)if(w={values:P?A:O(d),keys:m?A:O(p),entries:N},x)for(S in w)S in F||o(F,S,w[S]);else i(i.P+i.F*(v||j),n,w);return w}},function(t,n,r){var e=r(20),i=r(100),o=r(34),u=r(39)("IE_PROTO"),c=function(){},f="prototype",a=function(){var t,n=r(57)("iframe"),e=o.length;for(n.style.display="none",r(93).appendChild(n),n.src="javascript:",t=n.contentWindow.document,t.open(),t.write("<script>document.F=Object<\/script>"),t.close(),a=t.F;e--;)delete a[f][o[e]];return a()};t.exports=Object.create||function(t,n){var r;return null!==t?(c[f]=e(t),r=new c,c[f]=null,r[u]=t):r=a(),void 0===n?r:i(r,n)}},function(t,n,r){var e=r(63),i=r(34).concat("length","prototype");n.f=Object.getOwnPropertyNames||function(t){return e(t,i)}},function(t,n){n.f=Object.getOwnPropertySymbols},function(t,n,r){var e=r(8),i=r(9),o=r(90)(!1),u=r(39)("IE_PROTO");t.exports=function(t,n){var r,c=i(t),f=0,a=[];for(r in c)r!=u&&e(c,r)&&a.push(r);for(;n.length>f;)e(c,r=n[f++])&&(~o(a,r)||a.push(r));return a}},function(t,n,r){t.exports=r(13)},function(t,n,r){var e=r(76)("meta"),i=r(6),o=r(24),u=r(11).f,c=0,f=Object.isExtensible||function(){return!0},a=!r(4)(function(){return f(Object.preventExtensions({}))}),s=function(t){u(t,e,{value:{i:"O"+ ++c,w:{}}})},l=function(t,n){if(!i(t))return"symbol"==typeof t?t:("string"==typeof t?"S":"P")+t;if(!o(t,e)){if(!f(t))return"F";if(!n)return"E";s(t)}return t[e].i},h=function(t,n){if(!o(t,e)){if(!f(t))return!0;if(!n)return!1;s(t)}return t[e].w},v=function(t){return a&&p.NEED&&f(t)&&!o(t,e)&&s(t),t},p=t.exports={KEY:e,NEED:!1,fastKey:l,getWeak:h,onFreeze:v}},function(t,n){t.exports=function(t,n){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:n}}},function(t,n){var r=Math.ceil,e=Math.floor;t.exports=function(t){return isNaN(t=+t)?0:(t>0?e:r)(t)}},function(t,n){t.exports=function(t,n,r,e){if(!(t instanceof n)||void 0!==e&&e in t)throw TypeError(r+": incorrect invocation!");return t}},function(t,n){t.exports=!1},function(t,n,r){var e=r(2),i=r(173),o=r(133),u=r(145)("IE_PROTO"),c=function(){},f="prototype",a=function(){var t,n=r(132)("iframe"),e=o.length;for(n.style.display="none",r(135).appendChild(n),n.src="javascript:",t=n.contentWindow.document,t.open(),t.write("<script>document.F=Object<\/script>"),t.close(),a=t.F;e--;)delete a[f][o[e]];return a()};t.exports=Object.create||function(t,n){var r;return null!==t?(c[f]=e(t),r=new c,c[f]=null,r[u]=t):r=a(),void 0===n?r:i(r,n)}},function(t,n,r){var e=r(175),i=r(133).concat("length","prototype");n.f=Object.getOwnPropertyNames||function(t){return e(t,i)}},function(t,n,r){var e=r(175),i=r(133);t.exports=Object.keys||function(t){return e(t,i)}},function(t,n,r){var e=r(28);t.exports=function(t,n,r){for(var i in n)e(t,i,n[i],r);return t}},function(t,n,r){"use strict";var e=r(3),i=r(11),o=r(10),u=r(7)("species");t.exports=function(t){var n=e[t];o&&n&&!n[u]&&i.f(n,u,{configurable:!0,get:function(){return this}})}},function(t,n,r){var e=r(67),i=Math.max,o=Math.min;t.exports=function(t,n){return t=e(t),t<0?i(t+n,0):o(t,n)}},function(t,n){var r=0,e=Math.random();t.exports=function(t){return"Symbol(".concat(void 0===t?"":t,")_",(++r+e).toString(36))}},function(t,n,r){var e=r(33);t.exports=function(t){return Object(e(t))}},function(t,n,r){var e=r(7)("unscopables"),i=Array.prototype;void 0==i[e]&&r(27)(i,e,{}),t.exports=function(t){i[e][t]=!0}},function(t,n,r){var e=r(53),i=r(169),o=r(137),u=r(2),c=r(16),f=r(154),a={},s={},n=t.exports=function(t,n,r,l,h){var v,p,d,y,g=h?function(){return t}:f(t),b=e(r,l,n?2:1),m=0;if("function"!=typeof g)throw TypeError(t+" is not iterable!");if(o(g)){for(v=c(t.length);v>m;m++)if((y=n?b(u(p=t[m])[0],p[1]):b(t[m]))===a||y===s)return y}else for(d=g.call(t);!(p=d.next()).done;)if((y=i(d,b,p.value,n))===a||y===s)return y};n.BREAK=a,n.RETURN=s},function(t,n){t.exports={}},function(t,n,r){var e=r(11).f,i=r(24),o=r(7)("toStringTag");t.exports=function(t,n,r){t&&!i(t=r?t:t.prototype,o)&&e(t,o,{configurable:!0,value:n})}},function(t,n,r){var e=r(1),i=r(46),o=r(4),u=r(150),c="["+u+"]",f="​",a=RegExp("^"+c+c+"*"),s=RegExp(c+c+"*$"),l=function(t,n,r){var i={},c=o(function(){return!!u[t]()||f[t]()!=f}),a=i[t]=c?n(h):u[t];r&&(i[r]=a),e(e.P+e.F*c,"String",i)},h=l.trim=function(t,n){return t=String(i(t)),1&n&&(t=t.replace(a,"")),2&n&&(t=t.replace(s,"")),t};t.exports=l},function(t,n,r){t.exports={default:r(86),__esModule:!0}},function(t,n,r){t.exports={default:r(87),__esModule:!0}},function(t,n,r){"use strict";function e(t){return t&&t.__esModule?t:{default:t}}n.__esModule=!0;var i=r(84),o=e(i),u=r(83),c=e(u),f="function"==typeof c.default&&"symbol"==typeof o.default?function(t){return typeof t}:function(t){return t&&"function"==typeof c.default&&t.constructor===c.default&&t!==c.default.prototype?"symbol":typeof t};n.default="function"==typeof c.default&&"symbol"===f(o.default)?function(t){return void 0===t?"undefined":f(t)}:function(t){return t&&"function"==typeof c.default&&t.constructor===c.default&&t!==c.default.prototype?"symbol":void 0===t?"undefined":f(t)}},function(t,n,r){r(110),r(108),r(111),r(112),t.exports=r(25).Symbol},function(t,n,r){r(109),r(113),t.exports=r(44).f("iterator")},function(t,n){t.exports=function(t){if("function"!=typeof t)throw TypeError(t+" is not a function!");return t}},function(t,n){t.exports=function(){}},function(t,n,r){var e=r(9),i=r(106),o=r(105);t.exports=function(t){return function(n,r,u){var c,f=e(n),a=i(f.length),s=o(u,a);if(t&&r!=r){for(;a>s;)if((c=f[s++])!=c)return!0}else for(;a>s;s++)if((t||s in f)&&f[s]===r)return t||s||0;return!t&&-1}}},function(t,n,r){var e=r(88);t.exports=function(t,n,r){if(e(t),void 0===n)return t;switch(r){case 1:return function(r){return t.call(n,r)};case 2:return function(r,e){return t.call(n,r,e)};case 3:return function(r,e,i){return t.call(n,r,e,i)}}return function(){return t.apply(n,arguments)}}},function(t,n,r){var e=r(19),i=r(62),o=r(37);t.exports=function(t){var n=e(t),r=i.f;if(r)for(var u,c=r(t),f=o.f,a=0;c.length>a;)f.call(t,u=c[a++])&&n.push(u);return n}},function(t,n,r){t.exports=r(5).document&&document.documentElement},function(t,n,r){var e=r(56);t.exports=Object("z").propertyIsEnumerable(0)?Object:function(t){return"String"==e(t)?t.split(""):Object(t)}},function(t,n,r){var e=r(56);t.exports=Array.isArray||function(t){return"Array"==e(t)}},function(t,n,r){"use strict";var e=r(60),i=r(22),o=r(38),u={};r(13)(u,r(15)("iterator"),function(){return this}),t.exports=function(t,n,r){t.prototype=e(u,{next:i(1,r)}),o(t,n+" Iterator")}},function(t,n){t.exports=function(t,n){return{value:n,done:!!t}}},function(t,n,r){var e=r(19),i=r(9);t.exports=function(t,n){for(var r,o=i(t),u=e(o),c=u.length,f=0;c>f;)if(o[r=u[f++]]===n)return r}},function(t,n,r){var e=r(23)("meta"),i=r(21),o=r(8),u=r(14).f,c=0,f=Object.isExtensible||function(){return!0},a=!r(18)(function(){return f(Object.preventExtensions({}))}),s=function(t){u(t,e,{value:{i:"O"+ ++c,w:{}}})},l=function(t,n){if(!i(t))return"symbol"==typeof t?t:("string"==typeof t?"S":"P")+t;if(!o(t,e)){if(!f(t))return"F";if(!n)return"E";s(t)}return t[e].i},h=function(t,n){if(!o(t,e)){if(!f(t))return!0;if(!n)return!1;s(t)}return t[e].w},v=function(t){return a&&p.NEED&&f(t)&&!o(t,e)&&s(t),t},p=t.exports={KEY:e,NEED:!1,fastKey:l,getWeak:h,onFreeze:v}},function(t,n,r){var e=r(14),i=r(20),o=r(19);t.exports=r(12)?Object.defineProperties:function(t,n){i(t);for(var r,u=o(n),c=u.length,f=0;c>f;)e.f(t,r=u[f++],n[r]);return t}},function(t,n,r){var e=r(37),i=r(22),o=r(9),u=r(42),c=r(8),f=r(58),a=Object.getOwnPropertyDescriptor;n.f=r(12)?a:function(t,n){if(t=o(t),n=u(n,!0),f)try{return a(t,n)}catch(t){}if(c(t,n))return i(!e.f.call(t,n),t[n])}},function(t,n,r){var e=r(9),i=r(61).f,o={}.toString,u="object"==typeof window&&window&&Object.getOwnPropertyNames?Object.getOwnPropertyNames(window):[],c=function(t){try{return i(t)}catch(t){return u.slice()}};t.exports.f=function(t){return u&&"[object Window]"==o.call(t)?c(t):i(e(t))}},function(t,n,r){var e=r(8),i=r(77),o=r(39)("IE_PROTO"),u=Object.prototype;t.exports=Object.getPrototypeOf||function(t){return t=i(t),e(t,o)?t[o]:"function"==typeof t.constructor&&t instanceof t.constructor?t.constructor.prototype:t instanceof Object?u:null}},function(t,n,r){var e=r(41),i=r(33);t.exports=function(t){return function(n,r){var o,u,c=String(i(n)),f=e(r),a=c.length;return f<0||f>=a?t?"":void 0:(o=c.charCodeAt(f),o<55296||o>56319||f+1===a||(u=c.charCodeAt(f+1))<56320||u>57343?t?c.charAt(f):o:t?c.slice(f,f+2):u-56320+(o-55296<<10)+65536)}}},function(t,n,r){var e=r(41),i=Math.max,o=Math.min;t.exports=function(t,n){return t=e(t),t<0?i(t+n,0):o(t,n)}},function(t,n,r){var e=r(41),i=Math.min;t.exports=function(t){return t>0?i(e(t),9007199254740991):0}},function(t,n,r){"use strict";var e=r(89),i=r(97),o=r(35),u=r(9);t.exports=r(59)(Array,"Array",function(t,n){this._t=u(t),this._i=0,this._k=n},function(){var t=this._t,n=this._k,r=this._i++;return!t||r>=t.length?(this._t=void 0,i(1)):"keys"==n?i(0,r):"values"==n?i(0,t[r]):i(0,[r,t[r]])},"values"),o.Arguments=o.Array,e("keys"),e("values"),e("entries")},function(t,n){},function(t,n,r){"use strict";var e=r(104)(!0);r(59)(String,"String",function(t){this._t=String(t),this._i=0},function(){var t,n=this._t,r=this._i;return r>=n.length?{value:void 0,done:!0}:(t=e(n,r),this._i+=t.length,{value:t,done:!1})})},function(t,n,r){"use strict";var e=r(5),i=r(8),o=r(12),u=r(51),c=r(64),f=r(99).KEY,a=r(18),s=r(40),l=r(38),h=r(23),v=r(15),p=r(44),d=r(43),y=r(98),g=r(92),b=r(95),m=r(20),x=r(9),w=r(42),S=r(22),_=r(60),O=r(102),E=r(101),P=r(14),j=r(19),F=E.f,M=P.f,A=O.f,N=e.Symbol,T=e.JSON,I=T&&T.stringify,k="prototype",L=v("_hidden"),R=v("toPrimitive"),C={}.propertyIsEnumerable,D=s("symbol-registry"),U=s("symbols"),W=s("op-symbols"),G=Object[k],B="function"==typeof N,V=e.QObject,z=!V||!V[k]||!V[k].findChild,q=o&&a(function(){return 7!=_(M({},"a",{get:function(){return M(this,"a",{value:7}).a}})).a})?function(t,n,r){var e=F(G,n);e&&delete G[n],M(t,n,r),e&&t!==G&&M(G,n,e)}:M,K=function(t){var n=U[t]=_(N[k]);return n._k=t,n},J=B&&"symbol"==typeof N.iterator?function(t){return"symbol"==typeof t}:function(t){return t instanceof N},Y=function(t,n,r){return t===G&&Y(W,n,r),m(t),n=w(n,!0),m(r),i(U,n)?(r.enumerable?(i(t,L)&&t[L][n]&&(t[L][n]=!1),r=_(r,{enumerable:S(0,!1)})):(i(t,L)||M(t,L,S(1,{})),t[L][n]=!0),q(t,n,r)):M(t,n,r)},H=function(t,n){m(t);for(var r,e=g(n=x(n)),i=0,o=e.length;o>i;)Y(t,r=e[i++],n[r]);return t},$=function(t,n){return void 0===n?_(t):H(_(t),n)},X=function(t){var n=C.call(this,t=w(t,!0));return!(this===G&&i(U,t)&&!i(W,t))&&(!(n||!i(this,t)||!i(U,t)||i(this,L)&&this[L][t])||n)},Q=function(t,n){if(t=x(t),n=w(n,!0),t!==G||!i(U,n)||i(W,n)){var r=F(t,n);return!r||!i(U,n)||i(t,L)&&t[L][n]||(r.enumerable=!0),r}},Z=function(t){for(var n,r=A(x(t)),e=[],o=0;r.length>o;)i(U,n=r[o++])||n==L||n==f||e.push(n);return e},tt=function(t){for(var n,r=t===G,e=A(r?W:x(t)),o=[],u=0;e.length>u;)!i(U,n=e[u++])||r&&!i(G,n)||o.push(U[n]);return o};B||(N=function(){if(this instanceof N)throw TypeError("Symbol is not a constructor!");var t=h(arguments.length>0?arguments[0]:void 0),n=function(r){this===G&&n.call(W,r),i(this,L)&&i(this[L],t)&&(this[L][t]=!1),q(this,t,S(1,r))};return o&&z&&q(G,t,{configurable:!0,set:n}),K(t)},c(N[k],"toString",function(){return this._k}),E.f=Q,P.f=Y,r(61).f=O.f=Z,r(37).f=X,r(62).f=tt,o&&!r(36)&&c(G,"propertyIsEnumerable",X,!0),p.f=function(t){return K(v(t))}),u(u.G+u.W+u.F*!B,{Symbol:N});for(var nt="hasInstance,isConcatSpreadable,iterator,match,replace,search,species,split,toPrimitive,toStringTag,unscopables".split(","),rt=0;nt.length>rt;)v(nt[rt++]);for(var nt=j(v.store),rt=0;nt.length>rt;)d(nt[rt++]);u(u.S+u.F*!B,"Symbol",{for:function(t){return i(D,t+="")?D[t]:D[t]=N(t)},keyFor:function(t){if(J(t))return y(D,t);throw TypeError(t+" is not a symbol!")},useSetter:function(){z=!0},useSimple:function(){z=!1}}),u(u.S+u.F*!B,"Object",{create:$,defineProperty:Y,defineProperties:H,getOwnPropertyDescriptor:Q,getOwnPropertyNames:Z,getOwnPropertySymbols:tt}),T&&u(u.S+u.F*(!B||a(function(){var t=N();return"[null]"!=I([t])||"{}"!=I({a:t})||"{}"!=I(Object(t))})),"JSON",{stringify:function(t){if(void 0!==t&&!J(t)){for(var n,r,e=[t],i=1;arguments.length>i;)e.push(arguments[i++]);return n=e[1],"function"==typeof n&&(r=n),!r&&b(n)||(n=function(t,n){if(r&&(n=r.call(this,t,n)),!J(n))return n}),e[1]=n,I.apply(T,e)}}}),N[k][R]||r(13)(N[k],R,N[k].valueOf),l(N,"Symbol"),l(Math,"Math",!0),l(e.JSON,"JSON",!0)},function(t,n,r){r(43)("asyncIterator")},function(t,n,r){r(43)("observable")},function(t,n,r){r(107);for(var e=r(5),i=r(13),o=r(35),u=r(15)("toStringTag"),c=["NodeList","DOMTokenList","MediaList","StyleSheetList","CSSRuleList"],f=0;f<5;f++){var a=c[f],s=e[a],l=s&&s.prototype;l&&!l[u]&&i(l,u,a),o[a]=o.Array}},function(t,n,r){var e=r(45),i=r(7)("toStringTag"),o="Arguments"==e(function(){return arguments}()),u=function(t,n){try{return t[n]}catch(t){}};t.exports=function(t){var n,r,c;return void 0===t?"Undefined":null===t?"Null":"string"==typeof(r=u(n=Object(t),i))?r:o?e(n):"Object"==(c=e(n))&&"function"==typeof n.callee?"Arguments":c}},function(t,n,r){var e=r(45);t.exports=Object("z").propertyIsEnumerable(0)?Object:function(t){return"String"==e(t)?t.split(""):Object(t)}},function(t,n){n.f={}.propertyIsEnumerable},function(t,n,r){var e=r(30),i=r(16),o=r(75);t.exports=function(t){return function(n,r,u){var c,f=e(n),a=i(f.length),s=o(u,a);if(t&&r!=r){for(;a>s;)if((c=f[s++])!=c)return!0}else for(;a>s;s++)if((t||s in f)&&f[s]===r)return t||s||0;return!t&&-1}}},function(t,n,r){"use strict";var e=r(3),i=r(1),o=r(28),u=r(73),c=r(65),f=r(79),a=r(68),s=r(6),l=r(4),h=r(123),v=r(81),p=r(136);t.exports=function(t,n,r,d,y,g){var b=e[t],m=b,x=y?"set":"add",w=m&&m.prototype,S={},_=function(t){var n=w[t];o(w,t,"delete"==t?function(t){return!(g&&!s(t))&&n.call(this,0===t?0:t)}:"has"==t?function(t){return!(g&&!s(t))&&n.call(this,0===t?0:t)}:"get"==t?function(t){return g&&!s(t)?void 0:n.call(this,0===t?0:t)}:"add"==t?function(t){return n.call(this,0===t?0:t),this}:function(t,r){return n.call(this,0===t?0:t,r),this})};if("function"==typeof m&&(g||w.forEach&&!l(function(){(new m).entries().next()}))){var O=new m,E=O[x](g?{}:-0,1)!=O,P=l(function(){O.has(1)}),j=h(function(t){new m(t)}),F=!g&&l(function(){for(var t=new m,n=5;n--;)t[x](n,n);return!t.has(-0)});j||(m=n(function(n,r){a(n,m,t);var e=p(new b,n,m);return void 0!=r&&f(r,y,e[x],e),e}),m.prototype=w,w.constructor=m),(P||F)&&(_("delete"),_("has"),y&&_("get")),(F||E)&&_(x),g&&w.clear&&delete w.clear}else m=d.getConstructor(n,t,y,x),u(m.prototype,r),c.NEED=!0;return v(m,t),S[t]=m,i(i.G+i.W+i.F*(m!=b),S),g||d.setStrong(m,t,y),m}},function(t,n,r){"use strict";var e=r(27),i=r(28),o=r(4),u=r(46),c=r(7);t.exports=function(t,n,r){var f=c(t),a=r(u,f,""[t]),s=a[0],l=a[1];o(function(){var n={};return n[f]=function(){return 7},7!=""[t](n)})&&(i(String.prototype,t,s),e(RegExp.prototype,f,2==n?function(t,n){return l.call(t,this,n)}:function(t){return l.call(t,this)}))}
},function(t,n,r){"use strict";var e=r(2);t.exports=function(){var t=e(this),n="";return t.global&&(n+="g"),t.ignoreCase&&(n+="i"),t.multiline&&(n+="m"),t.unicode&&(n+="u"),t.sticky&&(n+="y"),n}},function(t,n){t.exports=function(t,n,r){var e=void 0===r;switch(n.length){case 0:return e?t():t.call(r);case 1:return e?t(n[0]):t.call(r,n[0]);case 2:return e?t(n[0],n[1]):t.call(r,n[0],n[1]);case 3:return e?t(n[0],n[1],n[2]):t.call(r,n[0],n[1],n[2]);case 4:return e?t(n[0],n[1],n[2],n[3]):t.call(r,n[0],n[1],n[2],n[3])}return t.apply(r,n)}},function(t,n,r){var e=r(6),i=r(45),o=r(7)("match");t.exports=function(t){var n;return e(t)&&(void 0!==(n=t[o])?!!n:"RegExp"==i(t))}},function(t,n,r){var e=r(7)("iterator"),i=!1;try{var o=[7][e]();o.return=function(){i=!0},Array.from(o,function(){throw 2})}catch(t){}t.exports=function(t,n){if(!n&&!i)return!1;var r=!1;try{var o=[7],u=o[e]();u.next=function(){return{done:r=!0}},o[e]=function(){return u},t(o)}catch(t){}return r}},function(t,n,r){t.exports=r(69)||!r(4)(function(){var t=Math.random();__defineSetter__.call(null,t,function(){}),delete r(3)[t]})},function(t,n){n.f=Object.getOwnPropertySymbols},function(t,n,r){var e=r(3),i="__core-js_shared__",o=e[i]||(e[i]={});t.exports=function(t){return o[t]||(o[t]={})}},function(t,n,r){for(var e,i=r(3),o=r(27),u=r(76),c=u("typed_array"),f=u("view"),a=!(!i.ArrayBuffer||!i.DataView),s=a,l=0,h="Int8Array,Uint8Array,Uint8ClampedArray,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array".split(",");l<9;)(e=i[h[l++]])?(o(e.prototype,c,!0),o(e.prototype,f,!0)):s=!1;t.exports={ABV:a,CONSTR:s,TYPED:c,VIEW:f}},function(t,n){"use strict";var r={versions:function(){var t=window.navigator.userAgent;return{trident:t.indexOf("Trident")>-1,presto:t.indexOf("Presto")>-1,webKit:t.indexOf("AppleWebKit")>-1,gecko:t.indexOf("Gecko")>-1&&-1==t.indexOf("KHTML"),mobile:!!t.match(/AppleWebKit.*Mobile.*/),ios:!!t.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/),android:t.indexOf("Android")>-1||t.indexOf("Linux")>-1,iPhone:t.indexOf("iPhone")>-1||t.indexOf("Mac")>-1,iPad:t.indexOf("iPad")>-1,webApp:-1==t.indexOf("Safari"),weixin:-1==t.indexOf("MicroMessenger")}}()};t.exports=r},function(t,n,r){"use strict";var e=r(85),i=function(t){return t&&t.__esModule?t:{default:t}}(e),o=function(){function t(t,n,e){return n||e?String.fromCharCode(n||e):r[t]||t}function n(t){return e[t]}var r={"&quot;":'"',"&lt;":"<","&gt;":">","&amp;":"&","&nbsp;":" "},e={};for(var u in r)e[r[u]]=u;return r["&apos;"]="'",e["'"]="&#39;",{encode:function(t){return t?(""+t).replace(/['<> "&]/g,n).replace(/\r?\n/g,"<br/>").replace(/\s/g,"&nbsp;"):""},decode:function(n){return n?(""+n).replace(/<br\s*\/?>/gi,"\n").replace(/&quot;|&lt;|&gt;|&amp;|&nbsp;|&apos;|&#(\d+);|&#(\d+)/g,t).replace(/\u00a0/g," "):""},encodeBase16:function(t){if(!t)return t;t+="";for(var n=[],r=0,e=t.length;e>r;r++)n.push(t.charCodeAt(r).toString(16).toUpperCase());return n.join("")},encodeBase16forJSON:function(t){if(!t)return t;t=t.replace(/[\u4E00-\u9FBF]/gi,function(t){return escape(t).replace("%u","\\u")});for(var n=[],r=0,e=t.length;e>r;r++)n.push(t.charCodeAt(r).toString(16).toUpperCase());return n.join("")},decodeBase16:function(t){if(!t)return t;t+="";for(var n=[],r=0,e=t.length;e>r;r+=2)n.push(String.fromCharCode("0x"+t.slice(r,r+2)));return n.join("")},encodeObject:function(t){if(t instanceof Array)for(var n=0,r=t.length;r>n;n++)t[n]=o.encodeObject(t[n]);else if("object"==(void 0===t?"undefined":(0,i.default)(t)))for(var e in t)t[e]=o.encodeObject(t[e]);else if("string"==typeof t)return o.encode(t);return t},loadScript:function(t){var n=document.createElement("script");document.getElementsByTagName("body")[0].appendChild(n),n.setAttribute("src",t)},addLoadEvent:function(t){var n=window.onload;"function"!=typeof window.onload?window.onload=t:window.onload=function(){n(),t()}}}}();t.exports=o},function(t,n,r){"use strict";var e=r(17),i=r(75),o=r(16);t.exports=function(t){for(var n=e(this),r=o(n.length),u=arguments.length,c=i(u>1?arguments[1]:void 0,r),f=u>2?arguments[2]:void 0,a=void 0===f?r:i(f,r);a>c;)n[c++]=t;return n}},function(t,n,r){"use strict";var e=r(11),i=r(66);t.exports=function(t,n,r){n in t?e.f(t,n,i(0,r)):t[n]=r}},function(t,n,r){var e=r(6),i=r(3).document,o=e(i)&&e(i.createElement);t.exports=function(t){return o?i.createElement(t):{}}},function(t,n){t.exports="constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf".split(",")},function(t,n,r){var e=r(7)("match");t.exports=function(t){var n=/./;try{"/./"[t](n)}catch(r){try{return n[e]=!1,!"/./"[t](n)}catch(t){}}return!0}},function(t,n,r){t.exports=r(3).document&&document.documentElement},function(t,n,r){var e=r(6),i=r(144).set;t.exports=function(t,n,r){var o,u=n.constructor;return u!==r&&"function"==typeof u&&(o=u.prototype)!==r.prototype&&e(o)&&i&&i(t,o),t}},function(t,n,r){var e=r(80),i=r(7)("iterator"),o=Array.prototype;t.exports=function(t){return void 0!==t&&(e.Array===t||o[i]===t)}},function(t,n,r){var e=r(45);t.exports=Array.isArray||function(t){return"Array"==e(t)}},function(t,n,r){"use strict";var e=r(70),i=r(66),o=r(81),u={};r(27)(u,r(7)("iterator"),function(){return this}),t.exports=function(t,n,r){t.prototype=e(u,{next:i(1,r)}),o(t,n+" Iterator")}},function(t,n,r){"use strict";var e=r(69),i=r(1),o=r(28),u=r(27),c=r(24),f=r(80),a=r(139),s=r(81),l=r(32),h=r(7)("iterator"),v=!([].keys&&"next"in[].keys()),p="keys",d="values",y=function(){return this};t.exports=function(t,n,r,g,b,m,x){a(r,n,g);var w,S,_,O=function(t){if(!v&&t in F)return F[t];switch(t){case p:case d:return function(){return new r(this,t)}}return function(){return new r(this,t)}},E=n+" Iterator",P=b==d,j=!1,F=t.prototype,M=F[h]||F["@@iterator"]||b&&F[b],A=M||O(b),N=b?P?O("entries"):A:void 0,T="Array"==n?F.entries||M:M;if(T&&(_=l(T.call(new t)))!==Object.prototype&&(s(_,E,!0),e||c(_,h)||u(_,h,y)),P&&M&&M.name!==d&&(j=!0,A=function(){return M.call(this)}),e&&!x||!v&&!j&&F[h]||u(F,h,A),f[n]=A,f[E]=y,b)if(w={values:P?A:O(d),keys:m?A:O(p),entries:N},x)for(S in w)S in F||o(F,S,w[S]);else i(i.P+i.F*(v||j),n,w);return w}},function(t,n){var r=Math.expm1;t.exports=!r||r(10)>22025.465794806718||r(10)<22025.465794806718||-2e-17!=r(-2e-17)?function(t){return 0==(t=+t)?t:t>-1e-6&&t<1e-6?t+t*t/2:Math.exp(t)-1}:r},function(t,n){t.exports=Math.sign||function(t){return 0==(t=+t)||t!=t?t:t<0?-1:1}},function(t,n,r){var e=r(3),i=r(151).set,o=e.MutationObserver||e.WebKitMutationObserver,u=e.process,c=e.Promise,f="process"==r(45)(u);t.exports=function(){var t,n,r,a=function(){var e,i;for(f&&(e=u.domain)&&e.exit();t;){i=t.fn,t=t.next;try{i()}catch(e){throw t?r():n=void 0,e}}n=void 0,e&&e.enter()};if(f)r=function(){u.nextTick(a)};else if(o){var s=!0,l=document.createTextNode("");new o(a).observe(l,{characterData:!0}),r=function(){l.data=s=!s}}else if(c&&c.resolve){var h=c.resolve();r=function(){h.then(a)}}else r=function(){i.call(e,a)};return function(e){var i={fn:e,next:void 0};n&&(n.next=i),t||(t=i,r()),n=i}}},function(t,n,r){var e=r(6),i=r(2),o=function(t,n){if(i(t),!e(n)&&null!==n)throw TypeError(n+": can't set as prototype!")};t.exports={set:Object.setPrototypeOf||("__proto__"in{}?function(t,n,e){try{e=r(53)(Function.call,r(31).f(Object.prototype,"__proto__").set,2),e(t,[]),n=!(t instanceof Array)}catch(t){n=!0}return function(t,r){return o(t,r),n?t.__proto__=r:e(t,r),t}}({},!1):void 0),check:o}},function(t,n,r){var e=r(126)("keys"),i=r(76);t.exports=function(t){return e[t]||(e[t]=i(t))}},function(t,n,r){var e=r(2),i=r(26),o=r(7)("species");t.exports=function(t,n){var r,u=e(t).constructor;return void 0===u||void 0==(r=e(u)[o])?n:i(r)}},function(t,n,r){var e=r(67),i=r(46);t.exports=function(t){return function(n,r){var o,u,c=String(i(n)),f=e(r),a=c.length;return f<0||f>=a?t?"":void 0:(o=c.charCodeAt(f),o<55296||o>56319||f+1===a||(u=c.charCodeAt(f+1))<56320||u>57343?t?c.charAt(f):o:t?c.slice(f,f+2):u-56320+(o-55296<<10)+65536)}}},function(t,n,r){var e=r(122),i=r(46);t.exports=function(t,n,r){if(e(n))throw TypeError("String#"+r+" doesn't accept regex!");return String(i(t))}},function(t,n,r){"use strict";var e=r(67),i=r(46);t.exports=function(t){var n=String(i(this)),r="",o=e(t);if(o<0||o==1/0)throw RangeError("Count can't be negative");for(;o>0;(o>>>=1)&&(n+=n))1&o&&(r+=n);return r}},function(t,n){t.exports="\t\n\v\f\r   ᠎             　\u2028\u2029\ufeff"},function(t,n,r){var e,i,o,u=r(53),c=r(121),f=r(135),a=r(132),s=r(3),l=s.process,h=s.setImmediate,v=s.clearImmediate,p=s.MessageChannel,d=0,y={},g="onreadystatechange",b=function(){var t=+this;if(y.hasOwnProperty(t)){var n=y[t];delete y[t],n()}},m=function(t){b.call(t.data)};h&&v||(h=function(t){for(var n=[],r=1;arguments.length>r;)n.push(arguments[r++]);return y[++d]=function(){c("function"==typeof t?t:Function(t),n)},e(d),d},v=function(t){delete y[t]},"process"==r(45)(l)?e=function(t){l.nextTick(u(b,t,1))}:p?(i=new p,o=i.port2,i.port1.onmessage=m,e=u(o.postMessage,o,1)):s.addEventListener&&"function"==typeof postMessage&&!s.importScripts?(e=function(t){s.postMessage(t+"","*")},s.addEventListener("message",m,!1)):e=g in a("script")?function(t){f.appendChild(a("script"))[g]=function(){f.removeChild(this),b.call(t)}}:function(t){setTimeout(u(b,t,1),0)}),t.exports={set:h,clear:v}},function(t,n,r){"use strict";var e=r(3),i=r(10),o=r(69),u=r(127),c=r(27),f=r(73),a=r(4),s=r(68),l=r(67),h=r(16),v=r(71).f,p=r(11).f,d=r(130),y=r(81),g="ArrayBuffer",b="DataView",m="prototype",x="Wrong length!",w="Wrong index!",S=e[g],_=e[b],O=e.Math,E=e.RangeError,P=e.Infinity,j=S,F=O.abs,M=O.pow,A=O.floor,N=O.log,T=O.LN2,I="buffer",k="byteLength",L="byteOffset",R=i?"_b":I,C=i?"_l":k,D=i?"_o":L,U=function(t,n,r){var e,i,o,u=Array(r),c=8*r-n-1,f=(1<<c)-1,a=f>>1,s=23===n?M(2,-24)-M(2,-77):0,l=0,h=t<0||0===t&&1/t<0?1:0;for(t=F(t),t!=t||t===P?(i=t!=t?1:0,e=f):(e=A(N(t)/T),t*(o=M(2,-e))<1&&(e--,o*=2),t+=e+a>=1?s/o:s*M(2,1-a),t*o>=2&&(e++,o/=2),e+a>=f?(i=0,e=f):e+a>=1?(i=(t*o-1)*M(2,n),e+=a):(i=t*M(2,a-1)*M(2,n),e=0));n>=8;u[l++]=255&i,i/=256,n-=8);for(e=e<<n|i,c+=n;c>0;u[l++]=255&e,e/=256,c-=8);return u[--l]|=128*h,u},W=function(t,n,r){var e,i=8*r-n-1,o=(1<<i)-1,u=o>>1,c=i-7,f=r-1,a=t[f--],s=127&a;for(a>>=7;c>0;s=256*s+t[f],f--,c-=8);for(e=s&(1<<-c)-1,s>>=-c,c+=n;c>0;e=256*e+t[f],f--,c-=8);if(0===s)s=1-u;else{if(s===o)return e?NaN:a?-P:P;e+=M(2,n),s-=u}return(a?-1:1)*e*M(2,s-n)},G=function(t){return t[3]<<24|t[2]<<16|t[1]<<8|t[0]},B=function(t){return[255&t]},V=function(t){return[255&t,t>>8&255]},z=function(t){return[255&t,t>>8&255,t>>16&255,t>>24&255]},q=function(t){return U(t,52,8)},K=function(t){return U(t,23,4)},J=function(t,n,r){p(t[m],n,{get:function(){return this[r]}})},Y=function(t,n,r,e){var i=+r,o=l(i);if(i!=o||o<0||o+n>t[C])throw E(w);var u=t[R]._b,c=o+t[D],f=u.slice(c,c+n);return e?f:f.reverse()},H=function(t,n,r,e,i,o){var u=+r,c=l(u);if(u!=c||c<0||c+n>t[C])throw E(w);for(var f=t[R]._b,a=c+t[D],s=e(+i),h=0;h<n;h++)f[a+h]=s[o?h:n-h-1]},$=function(t,n){s(t,S,g);var r=+n,e=h(r);if(r!=e)throw E(x);return e};if(u.ABV){if(!a(function(){new S})||!a(function(){new S(.5)})){S=function(t){return new j($(this,t))};for(var X,Q=S[m]=j[m],Z=v(j),tt=0;Z.length>tt;)(X=Z[tt++])in S||c(S,X,j[X]);o||(Q.constructor=S)}var nt=new _(new S(2)),rt=_[m].setInt8;nt.setInt8(0,2147483648),nt.setInt8(1,2147483649),!nt.getInt8(0)&&nt.getInt8(1)||f(_[m],{setInt8:function(t,n){rt.call(this,t,n<<24>>24)},setUint8:function(t,n){rt.call(this,t,n<<24>>24)}},!0)}else S=function(t){var n=$(this,t);this._b=d.call(Array(n),0),this[C]=n},_=function(t,n,r){s(this,_,b),s(t,S,b);var e=t[C],i=l(n);if(i<0||i>e)throw E("Wrong offset!");if(r=void 0===r?e-i:h(r),i+r>e)throw E(x);this[R]=t,this[D]=i,this[C]=r},i&&(J(S,k,"_l"),J(_,I,"_b"),J(_,k,"_l"),J(_,L,"_o")),f(_[m],{getInt8:function(t){return Y(this,1,t)[0]<<24>>24},getUint8:function(t){return Y(this,1,t)[0]},getInt16:function(t){var n=Y(this,2,t,arguments[1]);return(n[1]<<8|n[0])<<16>>16},getUint16:function(t){var n=Y(this,2,t,arguments[1]);return n[1]<<8|n[0]},getInt32:function(t){return G(Y(this,4,t,arguments[1]))},getUint32:function(t){return G(Y(this,4,t,arguments[1]))>>>0},getFloat32:function(t){return W(Y(this,4,t,arguments[1]),23,4)},getFloat64:function(t){return W(Y(this,8,t,arguments[1]),52,8)},setInt8:function(t,n){H(this,1,t,B,n)},setUint8:function(t,n){H(this,1,t,B,n)},setInt16:function(t,n){H(this,2,t,V,n,arguments[2])},setUint16:function(t,n){H(this,2,t,V,n,arguments[2])},setInt32:function(t,n){H(this,4,t,z,n,arguments[2])},setUint32:function(t,n){H(this,4,t,z,n,arguments[2])},setFloat32:function(t,n){H(this,4,t,K,n,arguments[2])},setFloat64:function(t,n){H(this,8,t,q,n,arguments[2])}});y(S,g),y(_,b),c(_[m],u.VIEW,!0),n[g]=S,n[b]=_},function(t,n,r){var e=r(3),i=r(52),o=r(69),u=r(182),c=r(11).f;t.exports=function(t){var n=i.Symbol||(i.Symbol=o?{}:e.Symbol||{});"_"==t.charAt(0)||t in n||c(n,t,{value:u.f(t)})}},function(t,n,r){var e=r(114),i=r(7)("iterator"),o=r(80);t.exports=r(52).getIteratorMethod=function(t){if(void 0!=t)return t[i]||t["@@iterator"]||o[e(t)]}},function(t,n,r){"use strict";var e=r(78),i=r(170),o=r(80),u=r(30);t.exports=r(140)(Array,"Array",function(t,n){this._t=u(t),this._i=0,this._k=n},function(){var t=this._t,n=this._k,r=this._i++;return!t||r>=t.length?(this._t=void 0,i(1)):"keys"==n?i(0,r):"values"==n?i(0,t[r]):i(0,[r,t[r]])},"values"),o.Arguments=o.Array,e("keys"),e("values"),e("entries")},function(t,n){function r(t,n){t.classList?t.classList.add(n):t.className+=" "+n}t.exports=r},function(t,n){function r(t,n){if(t.classList)t.classList.remove(n);else{var r=new RegExp("(^|\\b)"+n.split(" ").join("|")+"(\\b|$)","gi");t.className=t.className.replace(r," ")}}t.exports=r},function(t,n){function r(){throw new Error("setTimeout has not been defined")}function e(){throw new Error("clearTimeout has not been defined")}function i(t){if(s===setTimeout)return setTimeout(t,0);if((s===r||!s)&&setTimeout)return s=setTimeout,setTimeout(t,0);try{return s(t,0)}catch(n){try{return s.call(null,t,0)}catch(n){return s.call(this,t,0)}}}function o(t){if(l===clearTimeout)return clearTimeout(t);if((l===e||!l)&&clearTimeout)return l=clearTimeout,clearTimeout(t);try{return l(t)}catch(n){try{return l.call(null,t)}catch(n){return l.call(this,t)}}}function u(){d&&v&&(d=!1,v.length?p=v.concat(p):y=-1,p.length&&c())}function c(){if(!d){var t=i(u);d=!0;for(var n=p.length;n;){for(v=p,p=[];++y<n;)v&&v[y].run();y=-1,n=p.length}v=null,d=!1,o(t)}}function f(t,n){this.fun=t,this.array=n}function a(){}var s,l,h=t.exports={};!function(){try{s="function"==typeof setTimeout?setTimeout:r}catch(t){s=r}try{l="function"==typeof clearTimeout?clearTimeout:e}catch(t){l=e}}();var v,p=[],d=!1,y=-1;h.nextTick=function(t){var n=new Array(arguments.length-1);if(arguments.length>1)for(var r=1;r<arguments.length;r++)n[r-1]=arguments[r];p.push(new f(t,n)),1!==p.length||d||i(c)},f.prototype.run=function(){this.fun.apply(null,this.array)},h.title="browser",h.browser=!0,h.env={},h.argv=[],h.version="",h.versions={},h.on=a,h.addListener=a,h.once=a,h.off=a,h.removeListener=a,h.removeAllListeners=a,h.emit=a,h.prependListener=a,h.prependOnceListener=a,h.listeners=function(t){return[]},h.binding=function(t){throw new Error("process.binding is not supported")},h.cwd=function(){return"/"},h.chdir=function(t){throw new Error("process.chdir is not supported")},h.umask=function(){return 0}},function(t,n,r){var e=r(45);t.exports=function(t,n){if("number"!=typeof t&&"Number"!=e(t))throw TypeError(n);return+t}},function(t,n,r){"use strict";var e=r(17),i=r(75),o=r(16);t.exports=[].copyWithin||function(t,n){var r=e(this),u=o(r.length),c=i(t,u),f=i(n,u),a=arguments.length>2?arguments[2]:void 0,s=Math.min((void 0===a?u:i(a,u))-f,u-c),l=1;for(f<c&&c<f+s&&(l=-1,f+=s-1,c+=s-1);s-- >0;)f in r?r[c]=r[f]:delete r[c],c+=l,f+=l;return r}},function(t,n,r){var e=r(79);t.exports=function(t,n){var r=[];return e(t,!1,r.push,r,n),r}},function(t,n,r){var e=r(26),i=r(17),o=r(115),u=r(16);t.exports=function(t,n,r,c,f){e(n);var a=i(t),s=o(a),l=u(a.length),h=f?l-1:0,v=f?-1:1;if(r<2)for(;;){if(h in s){c=s[h],h+=v;break}if(h+=v,f?h<0:l<=h)throw TypeError("Reduce of empty array with no initial value")}for(;f?h>=0:l>h;h+=v)h in s&&(c=n(c,s[h],h,a));return c}},function(t,n,r){"use strict";var e=r(26),i=r(6),o=r(121),u=[].slice,c={},f=function(t,n,r){if(!(n in c)){for(var e=[],i=0;i<n;i++)e[i]="a["+i+"]";c[n]=Function("F,a","return new F("+e.join(",")+")")}return c[n](t,r)};t.exports=Function.bind||function(t){var n=e(this),r=u.call(arguments,1),c=function(){var e=r.concat(u.call(arguments));return this instanceof c?f(n,e.length,e):o(n,e,t)};return i(n.prototype)&&(c.prototype=n.prototype),c}},function(t,n,r){"use strict";var e=r(11).f,i=r(70),o=r(73),u=r(53),c=r(68),f=r(46),a=r(79),s=r(140),l=r(170),h=r(74),v=r(10),p=r(65).fastKey,d=v?"_s":"size",y=function(t,n){var r,e=p(n);if("F"!==e)return t._i[e];for(r=t._f;r;r=r.n)if(r.k==n)return r};t.exports={getConstructor:function(t,n,r,s){var l=t(function(t,e){c(t,l,n,"_i"),t._i=i(null),t._f=void 0,t._l=void 0,t[d]=0,void 0!=e&&a(e,r,t[s],t)});return o(l.prototype,{clear:function(){for(var t=this,n=t._i,r=t._f;r;r=r.n)r.r=!0,r.p&&(r.p=r.p.n=void 0),delete n[r.i];t._f=t._l=void 0,t[d]=0},delete:function(t){var n=this,r=y(n,t);if(r){var e=r.n,i=r.p;delete n._i[r.i],r.r=!0,i&&(i.n=e),e&&(e.p=i),n._f==r&&(n._f=e),n._l==r&&(n._l=i),n[d]--}return!!r},forEach:function(t){c(this,l,"forEach");for(var n,r=u(t,arguments.length>1?arguments[1]:void 0,3);n=n?n.n:this._f;)for(r(n.v,n.k,this);n&&n.r;)n=n.p},has:function(t){return!!y(this,t)}}),v&&e(l.prototype,"size",{get:function(){return f(this[d])}}),l},def:function(t,n,r){var e,i,o=y(t,n);return o?o.v=r:(t._l=o={i:i=p(n,!0),k:n,v:r,p:e=t._l,n:void 0,r:!1},t._f||(t._f=o),e&&(e.n=o),t[d]++,"F"!==i&&(t._i[i]=o)),t},getEntry:y,setStrong:function(t,n,r){s(t,n,function(t,n){this._t=t,this._k=n,this._l=void 0},function(){for(var t=this,n=t._k,r=t._l;r&&r.r;)r=r.p;return t._t&&(t._l=r=r?r.n:t._t._f)?"keys"==n?l(0,r.k):"values"==n?l(0,r.v):l(0,[r.k,r.v]):(t._t=void 0,l(1))},r?"entries":"values",!r,!0),h(n)}}},function(t,n,r){var e=r(114),i=r(161);t.exports=function(t){return function(){if(e(this)!=t)throw TypeError(t+"#toJSON isn't generic");return i(this)}}},function(t,n,r){"use strict";var e=r(73),i=r(65).getWeak,o=r(2),u=r(6),c=r(68),f=r(79),a=r(48),s=r(24),l=a(5),h=a(6),v=0,p=function(t){return t._l||(t._l=new d)},d=function(){this.a=[]},y=function(t,n){return l(t.a,function(t){return t[0]===n})};d.prototype={get:function(t){var n=y(this,t);if(n)return n[1]},has:function(t){return!!y(this,t)},set:function(t,n){var r=y(this,t);r?r[1]=n:this.a.push([t,n])},delete:function(t){var n=h(this.a,function(n){return n[0]===t});return~n&&this.a.splice(n,1),!!~n}},t.exports={getConstructor:function(t,n,r,o){var a=t(function(t,e){c(t,a,n,"_i"),t._i=v++,t._l=void 0,void 0!=e&&f(e,r,t[o],t)});return e(a.prototype,{delete:function(t){if(!u(t))return!1;var n=i(t);return!0===n?p(this).delete(t):n&&s(n,this._i)&&delete n[this._i]},has:function(t){if(!u(t))return!1;var n=i(t);return!0===n?p(this).has(t):n&&s(n,this._i)}}),a},def:function(t,n,r){var e=i(o(n),!0);return!0===e?p(t).set(n,r):e[t._i]=r,t},ufstore:p}},function(t,n,r){t.exports=!r(10)&&!r(4)(function(){return 7!=Object.defineProperty(r(132)("div"),"a",{get:function(){return 7}}).a})},function(t,n,r){var e=r(6),i=Math.floor;t.exports=function(t){return!e(t)&&isFinite(t)&&i(t)===t}},function(t,n,r){var e=r(2);t.exports=function(t,n,r,i){try{return i?n(e(r)[0],r[1]):n(r)}catch(n){var o=t.return;throw void 0!==o&&e(o.call(t)),n}}},function(t,n){t.exports=function(t,n){return{value:n,done:!!t}}},function(t,n){t.exports=Math.log1p||function(t){return(t=+t)>-1e-8&&t<1e-8?t-t*t/2:Math.log(1+t)}},function(t,n,r){"use strict";var e=r(72),i=r(125),o=r(116),u=r(17),c=r(115),f=Object.assign;t.exports=!f||r(4)(function(){var t={},n={},r=Symbol(),e="abcdefghijklmnopqrst";return t[r]=7,e.split("").forEach(function(t){n[t]=t}),7!=f({},t)[r]||Object.keys(f({},n)).join("")!=e})?function(t,n){for(var r=u(t),f=arguments.length,a=1,s=i.f,l=o.f;f>a;)for(var h,v=c(arguments[a++]),p=s?e(v).concat(s(v)):e(v),d=p.length,y=0;d>y;)l.call(v,h=p[y++])&&(r[h]=v[h]);return r}:f},function(t,n,r){var e=r(11),i=r(2),o=r(72);t.exports=r(10)?Object.defineProperties:function(t,n){i(t);for(var r,u=o(n),c=u.length,f=0;c>f;)e.f(t,r=u[f++],n[r]);return t}},function(t,n,r){var e=r(30),i=r(71).f,o={}.toString,u="object"==typeof window&&window&&Object.getOwnPropertyNames?Object.getOwnPropertyNames(window):[],c=function(t){try{return i(t)}catch(t){return u.slice()}};t.exports.f=function(t){return u&&"[object Window]"==o.call(t)?c(t):i(e(t))}},function(t,n,r){var e=r(24),i=r(30),o=r(117)(!1),u=r(145)("IE_PROTO");t.exports=function(t,n){var r,c=i(t),f=0,a=[];for(r in c)r!=u&&e(c,r)&&a.push(r);for(;n.length>f;)e(c,r=n[f++])&&(~o(a,r)||a.push(r));return a}},function(t,n,r){var e=r(72),i=r(30),o=r(116).f;t.exports=function(t){return function(n){for(var r,u=i(n),c=e(u),f=c.length,a=0,s=[];f>a;)o.call(u,r=c[a++])&&s.push(t?[r,u[r]]:u[r]);return s}}},function(t,n,r){var e=r(71),i=r(125),o=r(2),u=r(3).Reflect;t.exports=u&&u.ownKeys||function(t){var n=e.f(o(t)),r=i.f;return r?n.concat(r(t)):n}},function(t,n,r){var e=r(3).parseFloat,i=r(82).trim;t.exports=1/e(r(150)+"-0")!=-1/0?function(t){var n=i(String(t),3),r=e(n);return 0===r&&"-"==n.charAt(0)?-0:r}:e},function(t,n,r){var e=r(3).parseInt,i=r(82).trim,o=r(150),u=/^[\-+]?0[xX]/;t.exports=8!==e(o+"08")||22!==e(o+"0x16")?function(t,n){var r=i(String(t),3);return e(r,n>>>0||(u.test(r)?16:10))}:e},function(t,n){t.exports=Object.is||function(t,n){return t===n?0!==t||1/t==1/n:t!=t&&n!=n}},function(t,n,r){var e=r(16),i=r(149),o=r(46);t.exports=function(t,n,r,u){var c=String(o(t)),f=c.length,a=void 0===r?" ":String(r),s=e(n);if(s<=f||""==a)return c;var l=s-f,h=i.call(a,Math.ceil(l/a.length));return h.length>l&&(h=h.slice(0,l)),u?h+c:c+h}},function(t,n,r){n.f=r(7)},function(t,n,r){"use strict";var e=r(164);t.exports=r(118)("Map",function(t){return function(){return t(this,arguments.length>0?arguments[0]:void 0)}},{get:function(t){var n=e.getEntry(this,t);return n&&n.v},set:function(t,n){return e.def(this,0===t?0:t,n)}},e,!0)},function(t,n,r){r(10)&&"g"!=/./g.flags&&r(11).f(RegExp.prototype,"flags",{configurable:!0,get:r(120)})},function(t,n,r){"use strict";var e=r(164);t.exports=r(118)("Set",function(t){return function(){return t(this,arguments.length>0?arguments[0]:void 0)}},{add:function(t){return e.def(this,t=0===t?0:t,t)}},e)},function(t,n,r){"use strict";var e,i=r(48)(0),o=r(28),u=r(65),c=r(172),f=r(166),a=r(6),s=u.getWeak,l=Object.isExtensible,h=f.ufstore,v={},p=function(t){return function(){return t(this,arguments.length>0?arguments[0]:void 0)}},d={get:function(t){if(a(t)){var n=s(t);return!0===n?h(this).get(t):n?n[this._i]:void 0}},set:function(t,n){return f.def(this,t,n)}},y=t.exports=r(118)("WeakMap",p,d,f,!0,!0);7!=(new y).set((Object.freeze||Object)(v),7).get(v)&&(e=f.getConstructor(p),c(e.prototype,d),u.NEED=!0,i(["delete","has","get","set"],function(t){var n=y.prototype,r=n[t];o(n,t,function(n,i){if(a(n)&&!l(n)){this._f||(this._f=new e);var o=this._f[t](n,i);return"set"==t?this:o}return r.call(this,n,i)})}))},,,,function(t,n){"use strict";function r(){var t=document.querySelector("#page-nav");if(t&&!document.querySelector("#page-nav .extend.prev")&&(t.innerHTML='<a class="extend prev disabled" rel="prev">&laquo; Prev</a>'+t.innerHTML),t&&!document.querySelector("#page-nav .extend.next")&&(t.innerHTML=t.innerHTML+'<a class="extend next disabled" rel="next">Next &raquo;</a>'),yiliaConfig&&yiliaConfig.open_in_new){document.querySelectorAll(".article-entry a:not(.article-more-a)").forEach(function(t){var n=t.getAttribute("target");n&&""!==n||t.setAttribute("target","_blank")})}if(yiliaConfig&&yiliaConfig.toc_hide_index){document.querySelectorAll(".toc-number").forEach(function(t){t.style.display="none"})}var n=document.querySelector("#js-aboutme");n&&0!==n.length&&(n.innerHTML=n.innerText)}t.exports={init:r}},function(t,n,r){"use strict";function e(t){return t&&t.__esModule?t:{default:t}}function i(t,n){var r=/\/|index.html/g;return t.replace(r,"")===n.replace(r,"")}function o(){for(var t=document.querySelectorAll(".js-header-menu li a"),n=window.location.pathname,r=0,e=t.length;r<e;r++){var o=t[r];i(n,o.getAttribute("href"))&&(0,h.default)(o,"active")}}function u(t){for(var n=t.offsetLeft,r=t.offsetParent;null!==r;)n+=r.offsetLeft,r=r.offsetParent;return n}function c(t){for(var n=t.offsetTop,r=t.offsetParent;null!==r;)n+=r.offsetTop,r=r.offsetParent;return n}function f(t,n,r,e,i){var o=u(t),f=c(t)-n;if(f-r<=i){var a=t.$newDom;a||(a=t.cloneNode(!0),(0,d.default)(t,a),t.$newDom=a,a.style.position="fixed",a.style.top=(r||f)+"px",a.style.left=o+"px",a.style.zIndex=e||2,a.style.width="100%",a.style.color="#fff"),a.style.visibility="visible",t.style.visibility="hidden"}else{t.style.visibility="visible";var s=t.$newDom;s&&(s.style.visibility="hidden")}}function a(){var t=document.querySelector(".js-overlay"),n=document.querySelector(".js-header-menu");f(t,document.body.scrollTop,-63,2,0),f(n,document.body.scrollTop,1,3,0)}function s(){document.querySelector("#container").addEventListener("scroll",function(t){a()}),window.addEventListener("scroll",function(t){a()}),a()}var l=r(156),h=e(l),v=r(157),p=(e(v),r(382)),d=e(p),y=r(128),g=e(y),b=r(190),m=e(b),x=r(129);(function(){g.default.versions.mobile&&window.screen.width<800&&(o(),s())})(),(0,x.addLoadEvent)(function(){m.default.init()}),t.exports={}},,,,function(t,n,r){(function(t){"use strict";function n(t,n,r){t[n]||Object[e](t,n,{writable:!0,configurable:!0,value:r})}if(r(381),r(391),r(198),t._babelPolyfill)throw new Error("only one instance of babel-polyfill is allowed");t._babelPolyfill=!0;var e="defineProperty";n(String.prototype,"padLeft","".padStart),n(String.prototype,"padRight","".padEnd),"pop,reverse,shift,keys,values,entries,indexOf,every,some,forEach,map,filter,find,findIndex,includes,join,slice,concat,push,splice,unshift,sort,lastIndexOf,reduce,reduceRight,copyWithin,fill".split(",").forEach(function(t){[][t]&&n(Array,t,Function.call.bind([][t]))})}).call(n,function(){return this}())},,,function(t,n,r){r(210),t.exports=r(52).RegExp.escape},,,,function(t,n,r){var e=r(6),i=r(138),o=r(7)("species");t.exports=function(t){var n;return i(t)&&(n=t.constructor,"function"!=typeof n||n!==Array&&!i(n.prototype)||(n=void 0),e(n)&&null===(n=n[o])&&(n=void 0)),void 0===n?Array:n}},function(t,n,r){var e=r(202);t.exports=function(t,n){return new(e(t))(n)}},function(t,n,r){"use strict";var e=r(2),i=r(50),o="number";t.exports=function(t){if("string"!==t&&t!==o&&"default"!==t)throw TypeError("Incorrect hint");return i(e(this),t!=o)}},function(t,n,r){var e=r(72),i=r(125),o=r(116);t.exports=function(t){var n=e(t),r=i.f;if(r)for(var u,c=r(t),f=o.f,a=0;c.length>a;)f.call(t,u=c[a++])&&n.push(u);return n}},function(t,n,r){var e=r(72),i=r(30);t.exports=function(t,n){for(var r,o=i(t),u=e(o),c=u.length,f=0;c>f;)if(o[r=u[f++]]===n)return r}},function(t,n,r){"use strict";var e=r(208),i=r(121),o=r(26);t.exports=function(){for(var t=o(this),n=arguments.length,r=Array(n),u=0,c=e._,f=!1;n>u;)(r[u]=arguments[u++])===c&&(f=!0);return function(){var e,o=this,u=arguments.length,a=0,s=0;if(!f&&!u)return i(t,r,o);if(e=r.slice(),f)for(;n>a;a++)e[a]===c&&(e[a]=arguments[s++]);for(;u>s;)e.push(arguments[s++]);return i(t,e,o)}}},function(t,n,r){t.exports=r(3)},function(t,n){t.exports=function(t,n){var r=n===Object(n)?function(t){return n[t]}:n;return function(n){return String(n).replace(t,r)}}},function(t,n,r){var e=r(1),i=r(209)(/[\\^$*+?.()|[\]{}]/g,"\\$&");e(e.S,"RegExp",{escape:function(t){return i(t)}})},function(t,n,r){var e=r(1);e(e.P,"Array",{copyWithin:r(160)}),r(78)("copyWithin")},function(t,n,r){"use strict";var e=r(1),i=r(48)(4);e(e.P+e.F*!r(47)([].every,!0),"Array",{every:function(t){return i(this,t,arguments[1])}})},function(t,n,r){var e=r(1);e(e.P,"Array",{fill:r(130)}),r(78)("fill")},function(t,n,r){"use strict";var e=r(1),i=r(48)(2);e(e.P+e.F*!r(47)([].filter,!0),"Array",{filter:function(t){return i(this,t,arguments[1])}})},function(t,n,r){"use strict";var e=r(1),i=r(48)(6),o="findIndex",u=!0;o in[]&&Array(1)[o](function(){u=!1}),e(e.P+e.F*u,"Array",{findIndex:function(t){return i(this,t,arguments.length>1?arguments[1]:void 0)}}),r(78)(o)},function(t,n,r){"use strict";var e=r(1),i=r(48)(5),o="find",u=!0;o in[]&&Array(1)[o](function(){u=!1}),e(e.P+e.F*u,"Array",{find:function(t){return i(this,t,arguments.length>1?arguments[1]:void 0)}}),r(78)(o)},function(t,n,r){"use strict";var e=r(1),i=r(48)(0),o=r(47)([].forEach,!0);e(e.P+e.F*!o,"Array",{forEach:function(t){return i(this,t,arguments[1])}})},function(t,n,r){"use strict";var e=r(53),i=r(1),o=r(17),u=r(169),c=r(137),f=r(16),a=r(131),s=r(154);i(i.S+i.F*!r(123)(function(t){Array.from(t)}),"Array",{from:function(t){var n,r,i,l,h=o(t),v="function"==typeof this?this:Array,p=arguments.length,d=p>1?arguments[1]:void 0,y=void 0!==d,g=0,b=s(h);if(y&&(d=e(d,p>2?arguments[2]:void 0,2)),void 0==b||v==Array&&c(b))for(n=f(h.length),r=new v(n);n>g;g++)a(r,g,y?d(h[g],g):h[g]);else for(l=b.call(h),r=new v;!(i=l.next()).done;g++)a(r,g,y?u(l,d,[i.value,g],!0):i.value);return r.length=g,r}})},function(t,n,r){"use strict";var e=r(1),i=r(117)(!1),o=[].indexOf,u=!!o&&1/[1].indexOf(1,-0)<0;e(e.P+e.F*(u||!r(47)(o)),"Array",{indexOf:function(t){return u?o.apply(this,arguments)||0:i(this,t,arguments[1])}})},function(t,n,r){var e=r(1);e(e.S,"Array",{isArray:r(138)})},function(t,n,r){"use strict";var e=r(1),i=r(30),o=[].join;e(e.P+e.F*(r(115)!=Object||!r(47)(o)),"Array",{join:function(t){return o.call(i(this),void 0===t?",":t)}})},function(t,n,r){"use strict";var e=r(1),i=r(30),o=r(67),u=r(16),c=[].lastIndexOf,f=!!c&&1/[1].lastIndexOf(1,-0)<0;e(e.P+e.F*(f||!r(47)(c)),"Array",{lastIndexOf:function(t){if(f)return c.apply(this,arguments)||0;var n=i(this),r=u(n.length),e=r-1;for(arguments.length>1&&(e=Math.min(e,o(arguments[1]))),e<0&&(e=r+e);e>=0;e--)if(e in n&&n[e]===t)return e||0;return-1}})},function(t,n,r){"use strict";var e=r(1),i=r(48)(1);e(e.P+e.F*!r(47)([].map,!0),"Array",{map:function(t){return i(this,t,arguments[1])}})},function(t,n,r){"use strict";var e=r(1),i=r(131);e(e.S+e.F*r(4)(function(){function t(){}return!(Array.of.call(t)instanceof t)}),"Array",{of:function(){for(var t=0,n=arguments.length,r=new("function"==typeof this?this:Array)(n);n>t;)i(r,t,arguments[t++]);return r.length=n,r}})},function(t,n,r){"use strict";var e=r(1),i=r(162);e(e.P+e.F*!r(47)([].reduceRight,!0),"Array",{reduceRight:function(t){return i(this,t,arguments.length,arguments[1],!0)}})},function(t,n,r){"use strict";var e=r(1),i=r(162);e(e.P+e.F*!r(47)([].reduce,!0),"Array",{reduce:function(t){return i(this,t,arguments.length,arguments[1],!1)}})},function(t,n,r){"use strict";var e=r(1),i=r(135),o=r(45),u=r(75),c=r(16),f=[].slice;e(e.P+e.F*r(4)(function(){i&&f.call(i)}),"Array",{slice:function(t,n){var r=c(this.length),e=o(this);if(n=void 0===n?r:n,"Array"==e)return f.call(this,t,n);for(var i=u(t,r),a=u(n,r),s=c(a-i),l=Array(s),h=0;h<s;h++)l[h]="String"==e?this.charAt(i+h):this[i+h];return l}})},function(t,n,r){"use strict";var e=r(1),i=r(48)(3);e(e.P+e.F*!r(47)([].some,!0),"Array",{some:function(t){return i(this,t,arguments[1])}})},function(t,n,r){"use strict";var e=r(1),i=r(26),o=r(17),u=r(4),c=[].sort,f=[1,2,3];e(e.P+e.F*(u(function(){f.sort(void 0)})||!u(function(){f.sort(null)})||!r(47)(c)),"Array",{sort:function(t){return void 0===t?c.call(o(this)):c.call(o(this),i(t))}})},function(t,n,r){r(74)("Array")},function(t,n,r){var e=r(1);e(e.S,"Date",{now:function(){return(new Date).getTime()}})},function(t,n,r){"use strict";var e=r(1),i=r(4),o=Date.prototype.getTime,u=function(t){return t>9?t:"0"+t};e(e.P+e.F*(i(function(){return"0385-07-25T07:06:39.999Z"!=new Date(-5e13-1).toISOString()})||!i(function(){new Date(NaN).toISOString()})),"Date",{toISOString:function(){
if(!isFinite(o.call(this)))throw RangeError("Invalid time value");var t=this,n=t.getUTCFullYear(),r=t.getUTCMilliseconds(),e=n<0?"-":n>9999?"+":"";return e+("00000"+Math.abs(n)).slice(e?-6:-4)+"-"+u(t.getUTCMonth()+1)+"-"+u(t.getUTCDate())+"T"+u(t.getUTCHours())+":"+u(t.getUTCMinutes())+":"+u(t.getUTCSeconds())+"."+(r>99?r:"0"+u(r))+"Z"}})},function(t,n,r){"use strict";var e=r(1),i=r(17),o=r(50);e(e.P+e.F*r(4)(function(){return null!==new Date(NaN).toJSON()||1!==Date.prototype.toJSON.call({toISOString:function(){return 1}})}),"Date",{toJSON:function(t){var n=i(this),r=o(n);return"number"!=typeof r||isFinite(r)?n.toISOString():null}})},function(t,n,r){var e=r(7)("toPrimitive"),i=Date.prototype;e in i||r(27)(i,e,r(204))},function(t,n,r){var e=Date.prototype,i="Invalid Date",o="toString",u=e[o],c=e.getTime;new Date(NaN)+""!=i&&r(28)(e,o,function(){var t=c.call(this);return t===t?u.call(this):i})},function(t,n,r){var e=r(1);e(e.P,"Function",{bind:r(163)})},function(t,n,r){"use strict";var e=r(6),i=r(32),o=r(7)("hasInstance"),u=Function.prototype;o in u||r(11).f(u,o,{value:function(t){if("function"!=typeof this||!e(t))return!1;if(!e(this.prototype))return t instanceof this;for(;t=i(t);)if(this.prototype===t)return!0;return!1}})},function(t,n,r){var e=r(11).f,i=r(66),o=r(24),u=Function.prototype,c="name",f=Object.isExtensible||function(){return!0};c in u||r(10)&&e(u,c,{configurable:!0,get:function(){try{var t=this,n=(""+t).match(/^\s*function ([^ (]*)/)[1];return o(t,c)||!f(t)||e(t,c,i(5,n)),n}catch(t){return""}}})},function(t,n,r){var e=r(1),i=r(171),o=Math.sqrt,u=Math.acosh;e(e.S+e.F*!(u&&710==Math.floor(u(Number.MAX_VALUE))&&u(1/0)==1/0),"Math",{acosh:function(t){return(t=+t)<1?NaN:t>94906265.62425156?Math.log(t)+Math.LN2:i(t-1+o(t-1)*o(t+1))}})},function(t,n,r){function e(t){return isFinite(t=+t)&&0!=t?t<0?-e(-t):Math.log(t+Math.sqrt(t*t+1)):t}var i=r(1),o=Math.asinh;i(i.S+i.F*!(o&&1/o(0)>0),"Math",{asinh:e})},function(t,n,r){var e=r(1),i=Math.atanh;e(e.S+e.F*!(i&&1/i(-0)<0),"Math",{atanh:function(t){return 0==(t=+t)?t:Math.log((1+t)/(1-t))/2}})},function(t,n,r){var e=r(1),i=r(142);e(e.S,"Math",{cbrt:function(t){return i(t=+t)*Math.pow(Math.abs(t),1/3)}})},function(t,n,r){var e=r(1);e(e.S,"Math",{clz32:function(t){return(t>>>=0)?31-Math.floor(Math.log(t+.5)*Math.LOG2E):32}})},function(t,n,r){var e=r(1),i=Math.exp;e(e.S,"Math",{cosh:function(t){return(i(t=+t)+i(-t))/2}})},function(t,n,r){var e=r(1),i=r(141);e(e.S+e.F*(i!=Math.expm1),"Math",{expm1:i})},function(t,n,r){var e=r(1),i=r(142),o=Math.pow,u=o(2,-52),c=o(2,-23),f=o(2,127)*(2-c),a=o(2,-126),s=function(t){return t+1/u-1/u};e(e.S,"Math",{fround:function(t){var n,r,e=Math.abs(t),o=i(t);return e<a?o*s(e/a/c)*a*c:(n=(1+c/u)*e,r=n-(n-e),r>f||r!=r?o*(1/0):o*r)}})},function(t,n,r){var e=r(1),i=Math.abs;e(e.S,"Math",{hypot:function(t,n){for(var r,e,o=0,u=0,c=arguments.length,f=0;u<c;)r=i(arguments[u++]),f<r?(e=f/r,o=o*e*e+1,f=r):r>0?(e=r/f,o+=e*e):o+=r;return f===1/0?1/0:f*Math.sqrt(o)}})},function(t,n,r){var e=r(1),i=Math.imul;e(e.S+e.F*r(4)(function(){return-5!=i(4294967295,5)||2!=i.length}),"Math",{imul:function(t,n){var r=65535,e=+t,i=+n,o=r&e,u=r&i;return 0|o*u+((r&e>>>16)*u+o*(r&i>>>16)<<16>>>0)}})},function(t,n,r){var e=r(1);e(e.S,"Math",{log10:function(t){return Math.log(t)/Math.LN10}})},function(t,n,r){var e=r(1);e(e.S,"Math",{log1p:r(171)})},function(t,n,r){var e=r(1);e(e.S,"Math",{log2:function(t){return Math.log(t)/Math.LN2}})},function(t,n,r){var e=r(1);e(e.S,"Math",{sign:r(142)})},function(t,n,r){var e=r(1),i=r(141),o=Math.exp;e(e.S+e.F*r(4)(function(){return-2e-17!=!Math.sinh(-2e-17)}),"Math",{sinh:function(t){return Math.abs(t=+t)<1?(i(t)-i(-t))/2:(o(t-1)-o(-t-1))*(Math.E/2)}})},function(t,n,r){var e=r(1),i=r(141),o=Math.exp;e(e.S,"Math",{tanh:function(t){var n=i(t=+t),r=i(-t);return n==1/0?1:r==1/0?-1:(n-r)/(o(t)+o(-t))}})},function(t,n,r){var e=r(1);e(e.S,"Math",{trunc:function(t){return(t>0?Math.floor:Math.ceil)(t)}})},function(t,n,r){"use strict";var e=r(3),i=r(24),o=r(45),u=r(136),c=r(50),f=r(4),a=r(71).f,s=r(31).f,l=r(11).f,h=r(82).trim,v="Number",p=e[v],d=p,y=p.prototype,g=o(r(70)(y))==v,b="trim"in String.prototype,m=function(t){var n=c(t,!1);if("string"==typeof n&&n.length>2){n=b?n.trim():h(n,3);var r,e,i,o=n.charCodeAt(0);if(43===o||45===o){if(88===(r=n.charCodeAt(2))||120===r)return NaN}else if(48===o){switch(n.charCodeAt(1)){case 66:case 98:e=2,i=49;break;case 79:case 111:e=8,i=55;break;default:return+n}for(var u,f=n.slice(2),a=0,s=f.length;a<s;a++)if((u=f.charCodeAt(a))<48||u>i)return NaN;return parseInt(f,e)}}return+n};if(!p(" 0o1")||!p("0b1")||p("+0x1")){p=function(t){var n=arguments.length<1?0:t,r=this;return r instanceof p&&(g?f(function(){y.valueOf.call(r)}):o(r)!=v)?u(new d(m(n)),r,p):m(n)};for(var x,w=r(10)?a(d):"MAX_VALUE,MIN_VALUE,NaN,NEGATIVE_INFINITY,POSITIVE_INFINITY,EPSILON,isFinite,isInteger,isNaN,isSafeInteger,MAX_SAFE_INTEGER,MIN_SAFE_INTEGER,parseFloat,parseInt,isInteger".split(","),S=0;w.length>S;S++)i(d,x=w[S])&&!i(p,x)&&l(p,x,s(d,x));p.prototype=y,y.constructor=p,r(28)(e,v,p)}},function(t,n,r){var e=r(1);e(e.S,"Number",{EPSILON:Math.pow(2,-52)})},function(t,n,r){var e=r(1),i=r(3).isFinite;e(e.S,"Number",{isFinite:function(t){return"number"==typeof t&&i(t)}})},function(t,n,r){var e=r(1);e(e.S,"Number",{isInteger:r(168)})},function(t,n,r){var e=r(1);e(e.S,"Number",{isNaN:function(t){return t!=t}})},function(t,n,r){var e=r(1),i=r(168),o=Math.abs;e(e.S,"Number",{isSafeInteger:function(t){return i(t)&&o(t)<=9007199254740991}})},function(t,n,r){var e=r(1);e(e.S,"Number",{MAX_SAFE_INTEGER:9007199254740991})},function(t,n,r){var e=r(1);e(e.S,"Number",{MIN_SAFE_INTEGER:-9007199254740991})},function(t,n,r){var e=r(1),i=r(178);e(e.S+e.F*(Number.parseFloat!=i),"Number",{parseFloat:i})},function(t,n,r){var e=r(1),i=r(179);e(e.S+e.F*(Number.parseInt!=i),"Number",{parseInt:i})},function(t,n,r){"use strict";var e=r(1),i=r(67),o=r(159),u=r(149),c=1..toFixed,f=Math.floor,a=[0,0,0,0,0,0],s="Number.toFixed: incorrect invocation!",l="0",h=function(t,n){for(var r=-1,e=n;++r<6;)e+=t*a[r],a[r]=e%1e7,e=f(e/1e7)},v=function(t){for(var n=6,r=0;--n>=0;)r+=a[n],a[n]=f(r/t),r=r%t*1e7},p=function(){for(var t=6,n="";--t>=0;)if(""!==n||0===t||0!==a[t]){var r=String(a[t]);n=""===n?r:n+u.call(l,7-r.length)+r}return n},d=function(t,n,r){return 0===n?r:n%2==1?d(t,n-1,r*t):d(t*t,n/2,r)},y=function(t){for(var n=0,r=t;r>=4096;)n+=12,r/=4096;for(;r>=2;)n+=1,r/=2;return n};e(e.P+e.F*(!!c&&("0.000"!==8e-5.toFixed(3)||"1"!==.9.toFixed(0)||"1.25"!==1.255.toFixed(2)||"1000000000000000128"!==(0xde0b6b3a7640080).toFixed(0))||!r(4)(function(){c.call({})})),"Number",{toFixed:function(t){var n,r,e,c,f=o(this,s),a=i(t),g="",b=l;if(a<0||a>20)throw RangeError(s);if(f!=f)return"NaN";if(f<=-1e21||f>=1e21)return String(f);if(f<0&&(g="-",f=-f),f>1e-21)if(n=y(f*d(2,69,1))-69,r=n<0?f*d(2,-n,1):f/d(2,n,1),r*=4503599627370496,(n=52-n)>0){for(h(0,r),e=a;e>=7;)h(1e7,0),e-=7;for(h(d(10,e,1),0),e=n-1;e>=23;)v(1<<23),e-=23;v(1<<e),h(1,1),v(2),b=p()}else h(0,r),h(1<<-n,0),b=p()+u.call(l,a);return a>0?(c=b.length,b=g+(c<=a?"0."+u.call(l,a-c)+b:b.slice(0,c-a)+"."+b.slice(c-a))):b=g+b,b}})},function(t,n,r){"use strict";var e=r(1),i=r(4),o=r(159),u=1..toPrecision;e(e.P+e.F*(i(function(){return"1"!==u.call(1,void 0)})||!i(function(){u.call({})})),"Number",{toPrecision:function(t){var n=o(this,"Number#toPrecision: incorrect invocation!");return void 0===t?u.call(n):u.call(n,t)}})},function(t,n,r){var e=r(1);e(e.S+e.F,"Object",{assign:r(172)})},function(t,n,r){var e=r(1);e(e.S,"Object",{create:r(70)})},function(t,n,r){var e=r(1);e(e.S+e.F*!r(10),"Object",{defineProperties:r(173)})},function(t,n,r){var e=r(1);e(e.S+e.F*!r(10),"Object",{defineProperty:r(11).f})},function(t,n,r){var e=r(6),i=r(65).onFreeze;r(49)("freeze",function(t){return function(n){return t&&e(n)?t(i(n)):n}})},function(t,n,r){var e=r(30),i=r(31).f;r(49)("getOwnPropertyDescriptor",function(){return function(t,n){return i(e(t),n)}})},function(t,n,r){r(49)("getOwnPropertyNames",function(){return r(174).f})},function(t,n,r){var e=r(17),i=r(32);r(49)("getPrototypeOf",function(){return function(t){return i(e(t))}})},function(t,n,r){var e=r(6);r(49)("isExtensible",function(t){return function(n){return!!e(n)&&(!t||t(n))}})},function(t,n,r){var e=r(6);r(49)("isFrozen",function(t){return function(n){return!e(n)||!!t&&t(n)}})},function(t,n,r){var e=r(6);r(49)("isSealed",function(t){return function(n){return!e(n)||!!t&&t(n)}})},function(t,n,r){var e=r(1);e(e.S,"Object",{is:r(180)})},function(t,n,r){var e=r(17),i=r(72);r(49)("keys",function(){return function(t){return i(e(t))}})},function(t,n,r){var e=r(6),i=r(65).onFreeze;r(49)("preventExtensions",function(t){return function(n){return t&&e(n)?t(i(n)):n}})},function(t,n,r){var e=r(6),i=r(65).onFreeze;r(49)("seal",function(t){return function(n){return t&&e(n)?t(i(n)):n}})},function(t,n,r){var e=r(1);e(e.S,"Object",{setPrototypeOf:r(144).set})},function(t,n,r){"use strict";var e=r(114),i={};i[r(7)("toStringTag")]="z",i+""!="[object z]"&&r(28)(Object.prototype,"toString",function(){return"[object "+e(this)+"]"},!0)},function(t,n,r){var e=r(1),i=r(178);e(e.G+e.F*(parseFloat!=i),{parseFloat:i})},function(t,n,r){var e=r(1),i=r(179);e(e.G+e.F*(parseInt!=i),{parseInt:i})},function(t,n,r){"use strict";var e,i,o,u=r(69),c=r(3),f=r(53),a=r(114),s=r(1),l=r(6),h=r(26),v=r(68),p=r(79),d=r(146),y=r(151).set,g=r(143)(),b="Promise",m=c.TypeError,x=c.process,w=c[b],x=c.process,S="process"==a(x),_=function(){},O=!!function(){try{var t=w.resolve(1),n=(t.constructor={})[r(7)("species")]=function(t){t(_,_)};return(S||"function"==typeof PromiseRejectionEvent)&&t.then(_)instanceof n}catch(t){}}(),E=function(t,n){return t===n||t===w&&n===o},P=function(t){var n;return!(!l(t)||"function"!=typeof(n=t.then))&&n},j=function(t){return E(w,t)?new F(t):new i(t)},F=i=function(t){var n,r;this.promise=new t(function(t,e){if(void 0!==n||void 0!==r)throw m("Bad Promise constructor");n=t,r=e}),this.resolve=h(n),this.reject=h(r)},M=function(t){try{t()}catch(t){return{error:t}}},A=function(t,n){if(!t._n){t._n=!0;var r=t._c;g(function(){for(var e=t._v,i=1==t._s,o=0;r.length>o;)!function(n){var r,o,u=i?n.ok:n.fail,c=n.resolve,f=n.reject,a=n.domain;try{u?(i||(2==t._h&&I(t),t._h=1),!0===u?r=e:(a&&a.enter(),r=u(e),a&&a.exit()),r===n.promise?f(m("Promise-chain cycle")):(o=P(r))?o.call(r,c,f):c(r)):f(e)}catch(t){f(t)}}(r[o++]);t._c=[],t._n=!1,n&&!t._h&&N(t)})}},N=function(t){y.call(c,function(){var n,r,e,i=t._v;if(T(t)&&(n=M(function(){S?x.emit("unhandledRejection",i,t):(r=c.onunhandledrejection)?r({promise:t,reason:i}):(e=c.console)&&e.error&&e.error("Unhandled promise rejection",i)}),t._h=S||T(t)?2:1),t._a=void 0,n)throw n.error})},T=function(t){if(1==t._h)return!1;for(var n,r=t._a||t._c,e=0;r.length>e;)if(n=r[e++],n.fail||!T(n.promise))return!1;return!0},I=function(t){y.call(c,function(){var n;S?x.emit("rejectionHandled",t):(n=c.onrejectionhandled)&&n({promise:t,reason:t._v})})},k=function(t){var n=this;n._d||(n._d=!0,n=n._w||n,n._v=t,n._s=2,n._a||(n._a=n._c.slice()),A(n,!0))},L=function(t){var n,r=this;if(!r._d){r._d=!0,r=r._w||r;try{if(r===t)throw m("Promise can't be resolved itself");(n=P(t))?g(function(){var e={_w:r,_d:!1};try{n.call(t,f(L,e,1),f(k,e,1))}catch(t){k.call(e,t)}}):(r._v=t,r._s=1,A(r,!1))}catch(t){k.call({_w:r,_d:!1},t)}}};O||(w=function(t){v(this,w,b,"_h"),h(t),e.call(this);try{t(f(L,this,1),f(k,this,1))}catch(t){k.call(this,t)}},e=function(t){this._c=[],this._a=void 0,this._s=0,this._d=!1,this._v=void 0,this._h=0,this._n=!1},e.prototype=r(73)(w.prototype,{then:function(t,n){var r=j(d(this,w));return r.ok="function"!=typeof t||t,r.fail="function"==typeof n&&n,r.domain=S?x.domain:void 0,this._c.push(r),this._a&&this._a.push(r),this._s&&A(this,!1),r.promise},catch:function(t){return this.then(void 0,t)}}),F=function(){var t=new e;this.promise=t,this.resolve=f(L,t,1),this.reject=f(k,t,1)}),s(s.G+s.W+s.F*!O,{Promise:w}),r(81)(w,b),r(74)(b),o=r(52)[b],s(s.S+s.F*!O,b,{reject:function(t){var n=j(this);return(0,n.reject)(t),n.promise}}),s(s.S+s.F*(u||!O),b,{resolve:function(t){if(t instanceof w&&E(t.constructor,this))return t;var n=j(this);return(0,n.resolve)(t),n.promise}}),s(s.S+s.F*!(O&&r(123)(function(t){w.all(t).catch(_)})),b,{all:function(t){var n=this,r=j(n),e=r.resolve,i=r.reject,o=M(function(){var r=[],o=0,u=1;p(t,!1,function(t){var c=o++,f=!1;r.push(void 0),u++,n.resolve(t).then(function(t){f||(f=!0,r[c]=t,--u||e(r))},i)}),--u||e(r)});return o&&i(o.error),r.promise},race:function(t){var n=this,r=j(n),e=r.reject,i=M(function(){p(t,!1,function(t){n.resolve(t).then(r.resolve,e)})});return i&&e(i.error),r.promise}})},function(t,n,r){var e=r(1),i=r(26),o=r(2),u=(r(3).Reflect||{}).apply,c=Function.apply;e(e.S+e.F*!r(4)(function(){u(function(){})}),"Reflect",{apply:function(t,n,r){var e=i(t),f=o(r);return u?u(e,n,f):c.call(e,n,f)}})},function(t,n,r){var e=r(1),i=r(70),o=r(26),u=r(2),c=r(6),f=r(4),a=r(163),s=(r(3).Reflect||{}).construct,l=f(function(){function t(){}return!(s(function(){},[],t)instanceof t)}),h=!f(function(){s(function(){})});e(e.S+e.F*(l||h),"Reflect",{construct:function(t,n){o(t),u(n);var r=arguments.length<3?t:o(arguments[2]);if(h&&!l)return s(t,n,r);if(t==r){switch(n.length){case 0:return new t;case 1:return new t(n[0]);case 2:return new t(n[0],n[1]);case 3:return new t(n[0],n[1],n[2]);case 4:return new t(n[0],n[1],n[2],n[3])}var e=[null];return e.push.apply(e,n),new(a.apply(t,e))}var f=r.prototype,v=i(c(f)?f:Object.prototype),p=Function.apply.call(t,v,n);return c(p)?p:v}})},function(t,n,r){var e=r(11),i=r(1),o=r(2),u=r(50);i(i.S+i.F*r(4)(function(){Reflect.defineProperty(e.f({},1,{value:1}),1,{value:2})}),"Reflect",{defineProperty:function(t,n,r){o(t),n=u(n,!0),o(r);try{return e.f(t,n,r),!0}catch(t){return!1}}})},function(t,n,r){var e=r(1),i=r(31).f,o=r(2);e(e.S,"Reflect",{deleteProperty:function(t,n){var r=i(o(t),n);return!(r&&!r.configurable)&&delete t[n]}})},function(t,n,r){"use strict";var e=r(1),i=r(2),o=function(t){this._t=i(t),this._i=0;var n,r=this._k=[];for(n in t)r.push(n)};r(139)(o,"Object",function(){var t,n=this,r=n._k;do{if(n._i>=r.length)return{value:void 0,done:!0}}while(!((t=r[n._i++])in n._t));return{value:t,done:!1}}),e(e.S,"Reflect",{enumerate:function(t){return new o(t)}})},function(t,n,r){var e=r(31),i=r(1),o=r(2);i(i.S,"Reflect",{getOwnPropertyDescriptor:function(t,n){return e.f(o(t),n)}})},function(t,n,r){var e=r(1),i=r(32),o=r(2);e(e.S,"Reflect",{getPrototypeOf:function(t){return i(o(t))}})},function(t,n,r){function e(t,n){var r,c,s=arguments.length<3?t:arguments[2];return a(t)===s?t[n]:(r=i.f(t,n))?u(r,"value")?r.value:void 0!==r.get?r.get.call(s):void 0:f(c=o(t))?e(c,n,s):void 0}var i=r(31),o=r(32),u=r(24),c=r(1),f=r(6),a=r(2);c(c.S,"Reflect",{get:e})},function(t,n,r){var e=r(1);e(e.S,"Reflect",{has:function(t,n){return n in t}})},function(t,n,r){var e=r(1),i=r(2),o=Object.isExtensible;e(e.S,"Reflect",{isExtensible:function(t){return i(t),!o||o(t)}})},function(t,n,r){var e=r(1);e(e.S,"Reflect",{ownKeys:r(177)})},function(t,n,r){var e=r(1),i=r(2),o=Object.preventExtensions;e(e.S,"Reflect",{preventExtensions:function(t){i(t);try{return o&&o(t),!0}catch(t){return!1}}})},function(t,n,r){var e=r(1),i=r(144);i&&e(e.S,"Reflect",{setPrototypeOf:function(t,n){i.check(t,n);try{return i.set(t,n),!0}catch(t){return!1}}})},function(t,n,r){function e(t,n,r){var f,h,v=arguments.length<4?t:arguments[3],p=o.f(s(t),n);if(!p){if(l(h=u(t)))return e(h,n,r,v);p=a(0)}return c(p,"value")?!(!1===p.writable||!l(v)||(f=o.f(v,n)||a(0),f.value=r,i.f(v,n,f),0)):void 0!==p.set&&(p.set.call(v,r),!0)}var i=r(11),o=r(31),u=r(32),c=r(24),f=r(1),a=r(66),s=r(2),l=r(6);f(f.S,"Reflect",{set:e})},function(t,n,r){var e=r(3),i=r(136),o=r(11).f,u=r(71).f,c=r(122),f=r(120),a=e.RegExp,s=a,l=a.prototype,h=/a/g,v=/a/g,p=new a(h)!==h;if(r(10)&&(!p||r(4)(function(){return v[r(7)("match")]=!1,a(h)!=h||a(v)==v||"/a/i"!=a(h,"i")}))){a=function(t,n){var r=this instanceof a,e=c(t),o=void 0===n;return!r&&e&&t.constructor===a&&o?t:i(p?new s(e&&!o?t.source:t,n):s((e=t instanceof a)?t.source:t,e&&o?f.call(t):n),r?this:l,a)};for(var d=u(s),y=0;d.length>y;)!function(t){t in a||o(a,t,{configurable:!0,get:function(){return s[t]},set:function(n){s[t]=n}})}(d[y++]);l.constructor=a,a.prototype=l,r(28)(e,"RegExp",a)}r(74)("RegExp")},function(t,n,r){r(119)("match",1,function(t,n,r){return[function(r){"use strict";var e=t(this),i=void 0==r?void 0:r[n];return void 0!==i?i.call(r,e):new RegExp(r)[n](String(e))},r]})},function(t,n,r){r(119)("replace",2,function(t,n,r){return[function(e,i){"use strict";var o=t(this),u=void 0==e?void 0:e[n];return void 0!==u?u.call(e,o,i):r.call(String(o),e,i)},r]})},function(t,n,r){r(119)("search",1,function(t,n,r){return[function(r){"use strict";var e=t(this),i=void 0==r?void 0:r[n];return void 0!==i?i.call(r,e):new RegExp(r)[n](String(e))},r]})},function(t,n,r){r(119)("split",2,function(t,n,e){"use strict";var i=r(122),o=e,u=[].push,c="split",f="length",a="lastIndex";if("c"=="abbc"[c](/(b)*/)[1]||4!="test"[c](/(?:)/,-1)[f]||2!="ab"[c](/(?:ab)*/)[f]||4!="."[c](/(.?)(.?)/)[f]||"."[c](/()()/)[f]>1||""[c](/.?/)[f]){var s=void 0===/()??/.exec("")[1];e=function(t,n){var r=String(this);if(void 0===t&&0===n)return[];if(!i(t))return o.call(r,t,n);var e,c,l,h,v,p=[],d=(t.ignoreCase?"i":"")+(t.multiline?"m":"")+(t.unicode?"u":"")+(t.sticky?"y":""),y=0,g=void 0===n?4294967295:n>>>0,b=new RegExp(t.source,d+"g");for(s||(e=new RegExp("^"+b.source+"$(?!\\s)",d));(c=b.exec(r))&&!((l=c.index+c[0][f])>y&&(p.push(r.slice(y,c.index)),!s&&c[f]>1&&c[0].replace(e,function(){for(v=1;v<arguments[f]-2;v++)void 0===arguments[v]&&(c[v]=void 0)}),c[f]>1&&c.index<r[f]&&u.apply(p,c.slice(1)),h=c[0][f],y=l,p[f]>=g));)b[a]===c.index&&b[a]++;return y===r[f]?!h&&b.test("")||p.push(""):p.push(r.slice(y)),p[f]>g?p.slice(0,g):p}}else"0"[c](void 0,0)[f]&&(e=function(t,n){return void 0===t&&0===n?[]:o.call(this,t,n)});return[function(r,i){var o=t(this),u=void 0==r?void 0:r[n];return void 0!==u?u.call(r,o,i):e.call(String(o),r,i)},e]})},function(t,n,r){"use strict";r(184);var e=r(2),i=r(120),o=r(10),u="toString",c=/./[u],f=function(t){r(28)(RegExp.prototype,u,t,!0)};r(4)(function(){return"/a/b"!=c.call({source:"a",flags:"b"})})?f(function(){var t=e(this);return"/".concat(t.source,"/","flags"in t?t.flags:!o&&t instanceof RegExp?i.call(t):void 0)}):c.name!=u&&f(function(){return c.call(this)})},function(t,n,r){"use strict";r(29)("anchor",function(t){return function(n){return t(this,"a","name",n)}})},function(t,n,r){"use strict";r(29)("big",function(t){return function(){return t(this,"big","","")}})},function(t,n,r){"use strict";r(29)("blink",function(t){return function(){return t(this,"blink","","")}})},function(t,n,r){"use strict";r(29)("bold",function(t){return function(){return t(this,"b","","")}})},function(t,n,r){"use strict";var e=r(1),i=r(147)(!1);e(e.P,"String",{codePointAt:function(t){return i(this,t)}})},function(t,n,r){"use strict";var e=r(1),i=r(16),o=r(148),u="endsWith",c=""[u];e(e.P+e.F*r(134)(u),"String",{endsWith:function(t){var n=o(this,t,u),r=arguments.length>1?arguments[1]:void 0,e=i(n.length),f=void 0===r?e:Math.min(i(r),e),a=String(t);return c?c.call(n,a,f):n.slice(f-a.length,f)===a}})},function(t,n,r){"use strict";r(29)("fixed",function(t){return function(){return t(this,"tt","","")}})},function(t,n,r){"use strict";r(29)("fontcolor",function(t){return function(n){return t(this,"font","color",n)}})},function(t,n,r){"use strict";r(29)("fontsize",function(t){return function(n){return t(this,"font","size",n)}})},function(t,n,r){var e=r(1),i=r(75),o=String.fromCharCode,u=String.fromCodePoint;e(e.S+e.F*(!!u&&1!=u.length),"String",{fromCodePoint:function(t){for(var n,r=[],e=arguments.length,u=0;e>u;){if(n=+arguments[u++],i(n,1114111)!==n)throw RangeError(n+" is not a valid code point");r.push(n<65536?o(n):o(55296+((n-=65536)>>10),n%1024+56320))}return r.join("")}})},function(t,n,r){"use strict";var e=r(1),i=r(148),o="includes";e(e.P+e.F*r(134)(o),"String",{includes:function(t){return!!~i(this,t,o).indexOf(t,arguments.length>1?arguments[1]:void 0)}})},function(t,n,r){"use strict";r(29)("italics",function(t){return function(){return t(this,"i","","")}})},function(t,n,r){"use strict";var e=r(147)(!0);r(140)(String,"String",function(t){this._t=String(t),this._i=0},function(){var t,n=this._t,r=this._i;return r>=n.length?{value:void 0,done:!0}:(t=e(n,r),this._i+=t.length,{value:t,done:!1})})},function(t,n,r){"use strict";r(29)("link",function(t){return function(n){return t(this,"a","href",n)}})},function(t,n,r){var e=r(1),i=r(30),o=r(16);e(e.S,"String",{raw:function(t){for(var n=i(t.raw),r=o(n.length),e=arguments.length,u=[],c=0;r>c;)u.push(String(n[c++])),c<e&&u.push(String(arguments[c]));return u.join("")}})},function(t,n,r){var e=r(1);e(e.P,"String",{repeat:r(149)})},function(t,n,r){"use strict";r(29)("small",function(t){return function(){return t(this,"small","","")}})},function(t,n,r){"use strict";var e=r(1),i=r(16),o=r(148),u="startsWith",c=""[u];e(e.P+e.F*r(134)(u),"String",{startsWith:function(t){var n=o(this,t,u),r=i(Math.min(arguments.length>1?arguments[1]:void 0,n.length)),e=String(t);return c?c.call(n,e,r):n.slice(r,r+e.length)===e}})},function(t,n,r){"use strict";r(29)("strike",function(t){return function(){return t(this,"strike","","")}})},function(t,n,r){"use strict";r(29)("sub",function(t){return function(){return t(this,"sub","","")}})},function(t,n,r){"use strict";r(29)("sup",function(t){return function(){return t(this,"sup","","")}})},function(t,n,r){"use strict";r(82)("trim",function(t){return function(){return t(this,3)}})},function(t,n,r){"use strict";var e=r(3),i=r(24),o=r(10),u=r(1),c=r(28),f=r(65).KEY,a=r(4),s=r(126),l=r(81),h=r(76),v=r(7),p=r(182),d=r(153),y=r(206),g=r(205),b=r(138),m=r(2),x=r(30),w=r(50),S=r(66),_=r(70),O=r(174),E=r(31),P=r(11),j=r(72),F=E.f,M=P.f,A=O.f,N=e.Symbol,T=e.JSON,I=T&&T.stringify,k="prototype",L=v("_hidden"),R=v("toPrimitive"),C={}.propertyIsEnumerable,D=s("symbol-registry"),U=s("symbols"),W=s("op-symbols"),G=Object[k],B="function"==typeof N,V=e.QObject,z=!V||!V[k]||!V[k].findChild,q=o&&a(function(){return 7!=_(M({},"a",{get:function(){return M(this,"a",{value:7}).a}})).a})?function(t,n,r){var e=F(G,n);e&&delete G[n],M(t,n,r),e&&t!==G&&M(G,n,e)}:M,K=function(t){var n=U[t]=_(N[k]);return n._k=t,n},J=B&&"symbol"==typeof N.iterator?function(t){return"symbol"==typeof t}:function(t){return t instanceof N},Y=function(t,n,r){return t===G&&Y(W,n,r),m(t),n=w(n,!0),m(r),i(U,n)?(r.enumerable?(i(t,L)&&t[L][n]&&(t[L][n]=!1),r=_(r,{enumerable:S(0,!1)})):(i(t,L)||M(t,L,S(1,{})),t[L][n]=!0),q(t,n,r)):M(t,n,r)},H=function(t,n){m(t);for(var r,e=g(n=x(n)),i=0,o=e.length;o>i;)Y(t,r=e[i++],n[r]);return t},$=function(t,n){return void 0===n?_(t):H(_(t),n)},X=function(t){var n=C.call(this,t=w(t,!0));return!(this===G&&i(U,t)&&!i(W,t))&&(!(n||!i(this,t)||!i(U,t)||i(this,L)&&this[L][t])||n)},Q=function(t,n){if(t=x(t),n=w(n,!0),t!==G||!i(U,n)||i(W,n)){var r=F(t,n);return!r||!i(U,n)||i(t,L)&&t[L][n]||(r.enumerable=!0),r}},Z=function(t){for(var n,r=A(x(t)),e=[],o=0;r.length>o;)i(U,n=r[o++])||n==L||n==f||e.push(n);return e},tt=function(t){for(var n,r=t===G,e=A(r?W:x(t)),o=[],u=0;e.length>u;)!i(U,n=e[u++])||r&&!i(G,n)||o.push(U[n]);return o};B||(N=function(){if(this instanceof N)throw TypeError("Symbol is not a constructor!");var t=h(arguments.length>0?arguments[0]:void 0),n=function(r){this===G&&n.call(W,r),i(this,L)&&i(this[L],t)&&(this[L][t]=!1),q(this,t,S(1,r))};return o&&z&&q(G,t,{configurable:!0,set:n}),K(t)},c(N[k],"toString",function(){return this._k}),E.f=Q,P.f=Y,r(71).f=O.f=Z,r(116).f=X,r(125).f=tt,o&&!r(69)&&c(G,"propertyIsEnumerable",X,!0),p.f=function(t){return K(v(t))}),u(u.G+u.W+u.F*!B,{Symbol:N});for(var nt="hasInstance,isConcatSpreadable,iterator,match,replace,search,species,split,toPrimitive,toStringTag,unscopables".split(","),rt=0;nt.length>rt;)v(nt[rt++]);for(var nt=j(v.store),rt=0;nt.length>rt;)d(nt[rt++]);u(u.S+u.F*!B,"Symbol",{for:function(t){return i(D,t+="")?D[t]:D[t]=N(t)},keyFor:function(t){if(J(t))return y(D,t);throw TypeError(t+" is not a symbol!")},useSetter:function(){z=!0},useSimple:function(){z=!1}}),u(u.S+u.F*!B,"Object",{create:$,defineProperty:Y,defineProperties:H,getOwnPropertyDescriptor:Q,getOwnPropertyNames:Z,getOwnPropertySymbols:tt}),T&&u(u.S+u.F*(!B||a(function(){var t=N();return"[null]"!=I([t])||"{}"!=I({a:t})||"{}"!=I(Object(t))})),"JSON",{stringify:function(t){if(void 0!==t&&!J(t)){for(var n,r,e=[t],i=1;arguments.length>i;)e.push(arguments[i++]);return n=e[1],"function"==typeof n&&(r=n),!r&&b(n)||(n=function(t,n){if(r&&(n=r.call(this,t,n)),!J(n))return n}),e[1]=n,I.apply(T,e)}}}),N[k][R]||r(27)(N[k],R,N[k].valueOf),l(N,"Symbol"),l(Math,"Math",!0),l(e.JSON,"JSON",!0)},function(t,n,r){"use strict";var e=r(1),i=r(127),o=r(152),u=r(2),c=r(75),f=r(16),a=r(6),s=r(3).ArrayBuffer,l=r(146),h=o.ArrayBuffer,v=o.DataView,p=i.ABV&&s.isView,d=h.prototype.slice,y=i.VIEW,g="ArrayBuffer";e(e.G+e.W+e.F*(s!==h),{ArrayBuffer:h}),e(e.S+e.F*!i.CONSTR,g,{isView:function(t){return p&&p(t)||a(t)&&y in t}}),e(e.P+e.U+e.F*r(4)(function(){return!new h(2).slice(1,void 0).byteLength}),g,{slice:function(t,n){if(void 0!==d&&void 0===n)return d.call(u(this),t);for(var r=u(this).byteLength,e=c(t,r),i=c(void 0===n?r:n,r),o=new(l(this,h))(f(i-e)),a=new v(this),s=new v(o),p=0;e<i;)s.setUint8(p++,a.getUint8(e++));return o}}),r(74)(g)},function(t,n,r){var e=r(1);e(e.G+e.W+e.F*!r(127).ABV,{DataView:r(152).DataView})},function(t,n,r){r(55)("Float32",4,function(t){return function(n,r,e){return t(this,n,r,e)}})},function(t,n,r){r(55)("Float64",8,function(t){return function(n,r,e){return t(this,n,r,e)}})},function(t,n,r){r(55)("Int16",2,function(t){return function(n,r,e){return t(this,n,r,e)}})},function(t,n,r){r(55)("Int32",4,function(t){return function(n,r,e){return t(this,n,r,e)}})},function(t,n,r){r(55)("Int8",1,function(t){return function(n,r,e){return t(this,n,r,e)}})},function(t,n,r){r(55)("Uint16",2,function(t){return function(n,r,e){return t(this,n,r,e)}})},function(t,n,r){r(55)("Uint32",4,function(t){return function(n,r,e){return t(this,n,r,e)}})},function(t,n,r){r(55)("Uint8",1,function(t){return function(n,r,e){return t(this,n,r,e)}})},function(t,n,r){r(55)("Uint8",1,function(t){return function(n,r,e){return t(this,n,r,e)}},!0)},function(t,n,r){"use strict";var e=r(166);r(118)("WeakSet",function(t){return function(){return t(this,arguments.length>0?arguments[0]:void 0)}},{add:function(t){return e.def(this,t,!0)}},e,!1,!0)},function(t,n,r){"use strict";var e=r(1),i=r(117)(!0);e(e.P,"Array",{includes:function(t){return i(this,t,arguments.length>1?arguments[1]:void 0)}}),r(78)("includes")},function(t,n,r){var e=r(1),i=r(143)(),o=r(3).process,u="process"==r(45)(o);e(e.G,{asap:function(t){var n=u&&o.domain;i(n?n.bind(t):t)}})},function(t,n,r){var e=r(1),i=r(45);e(e.S,"Error",{isError:function(t){return"Error"===i(t)}})},function(t,n,r){var e=r(1);e(e.P+e.R,"Map",{toJSON:r(165)("Map")})},function(t,n,r){var e=r(1);e(e.S,"Math",{iaddh:function(t,n,r,e){var i=t>>>0,o=n>>>0,u=r>>>0;return o+(e>>>0)+((i&u|(i|u)&~(i+u>>>0))>>>31)|0}})},function(t,n,r){var e=r(1);e(e.S,"Math",{imulh:function(t,n){var r=65535,e=+t,i=+n,o=e&r,u=i&r,c=e>>16,f=i>>16,a=(c*u>>>0)+(o*u>>>16);return c*f+(a>>16)+((o*f>>>0)+(a&r)>>16)}})},function(t,n,r){var e=r(1);e(e.S,"Math",{isubh:function(t,n,r,e){var i=t>>>0,o=n>>>0,u=r>>>0;return o-(e>>>0)-((~i&u|~(i^u)&i-u>>>0)>>>31)|0}})},function(t,n,r){var e=r(1);e(e.S,"Math",{umulh:function(t,n){var r=65535,e=+t,i=+n,o=e&r,u=i&r,c=e>>>16,f=i>>>16,a=(c*u>>>0)+(o*u>>>16);return c*f+(a>>>16)+((o*f>>>0)+(a&r)>>>16)}})},function(t,n,r){"use strict";var e=r(1),i=r(17),o=r(26),u=r(11);r(10)&&e(e.P+r(124),"Object",{__defineGetter__:function(t,n){u.f(i(this),t,{get:o(n),enumerable:!0,configurable:!0})}})},function(t,n,r){"use strict";var e=r(1),i=r(17),o=r(26),u=r(11);r(10)&&e(e.P+r(124),"Object",{__defineSetter__:function(t,n){u.f(i(this),t,{set:o(n),enumerable:!0,configurable:!0})}})},function(t,n,r){var e=r(1),i=r(176)(!0);e(e.S,"Object",{entries:function(t){return i(t)}})},function(t,n,r){var e=r(1),i=r(177),o=r(30),u=r(31),c=r(131);e(e.S,"Object",{getOwnPropertyDescriptors:function(t){for(var n,r=o(t),e=u.f,f=i(r),a={},s=0;f.length>s;)c(a,n=f[s++],e(r,n));return a}})},function(t,n,r){"use strict";var e=r(1),i=r(17),o=r(50),u=r(32),c=r(31).f;r(10)&&e(e.P+r(124),"Object",{__lookupGetter__:function(t){var n,r=i(this),e=o(t,!0);do{if(n=c(r,e))return n.get}while(r=u(r))}})},function(t,n,r){"use strict";var e=r(1),i=r(17),o=r(50),u=r(32),c=r(31).f;r(10)&&e(e.P+r(124),"Object",{__lookupSetter__:function(t){var n,r=i(this),e=o(t,!0);do{if(n=c(r,e))return n.set}while(r=u(r))}})},function(t,n,r){var e=r(1),i=r(176)(!1);e(e.S,"Object",{values:function(t){return i(t)}})},function(t,n,r){"use strict";var e=r(1),i=r(3),o=r(52),u=r(143)(),c=r(7)("observable"),f=r(26),a=r(2),s=r(68),l=r(73),h=r(27),v=r(79),p=v.RETURN,d=function(t){return null==t?void 0:f(t)},y=function(t){var n=t._c;n&&(t._c=void 0,n())},g=function(t){return void 0===t._o},b=function(t){g(t)||(t._o=void 0,y(t))},m=function(t,n){a(t),this._c=void 0,this._o=t,t=new x(this);try{var r=n(t),e=r;null!=r&&("function"==typeof r.unsubscribe?r=function(){e.unsubscribe()}:f(r),this._c=r)}catch(n){return void t.error(n)}g(this)&&y(this)};m.prototype=l({},{unsubscribe:function(){b(this)}});var x=function(t){this._s=t};x.prototype=l({},{next:function(t){var n=this._s;if(!g(n)){var r=n._o;try{var e=d(r.next);if(e)return e.call(r,t)}catch(t){try{b(n)}finally{throw t}}}},error:function(t){var n=this._s;if(g(n))throw t;var r=n._o;n._o=void 0;try{var e=d(r.error);if(!e)throw t;t=e.call(r,t)}catch(t){try{y(n)}finally{throw t}}return y(n),t},complete:function(t){var n=this._s;if(!g(n)){var r=n._o;n._o=void 0;try{var e=d(r.complete);t=e?e.call(r,t):void 0}catch(t){try{y(n)}finally{throw t}}return y(n),t}}});var w=function(t){s(this,w,"Observable","_f")._f=f(t)};l(w.prototype,{subscribe:function(t){return new m(t,this._f)},forEach:function(t){var n=this;return new(o.Promise||i.Promise)(function(r,e){f(t);var i=n.subscribe({next:function(n){try{return t(n)}catch(t){e(t),i.unsubscribe()}},error:e,complete:r})})}}),l(w,{from:function(t){var n="function"==typeof this?this:w,r=d(a(t)[c]);if(r){var e=a(r.call(t));return e.constructor===n?e:new n(function(t){return e.subscribe(t)})}return new n(function(n){var r=!1;return u(function(){if(!r){try{if(v(t,!1,function(t){if(n.next(t),r)return p})===p)return}catch(t){if(r)throw t;return void n.error(t)}n.complete()}}),function(){r=!0}})},of:function(){for(var t=0,n=arguments.length,r=Array(n);t<n;)r[t]=arguments[t++];return new("function"==typeof this?this:w)(function(t){var n=!1;return u(function(){if(!n){for(var e=0;e<r.length;++e)if(t.next(r[e]),n)return;t.complete()}}),function(){n=!0}})}}),h(w.prototype,c,function(){return this}),e(e.G,{Observable:w}),r(74)("Observable")},function(t,n,r){var e=r(54),i=r(2),o=e.key,u=e.set;e.exp({defineMetadata:function(t,n,r,e){u(t,n,i(r),o(e))}})},function(t,n,r){var e=r(54),i=r(2),o=e.key,u=e.map,c=e.store;e.exp({deleteMetadata:function(t,n){var r=arguments.length<3?void 0:o(arguments[2]),e=u(i(n),r,!1);if(void 0===e||!e.delete(t))return!1;if(e.size)return!0;var f=c.get(n);return f.delete(r),!!f.size||c.delete(n)}})},function(t,n,r){var e=r(185),i=r(161),o=r(54),u=r(2),c=r(32),f=o.keys,a=o.key,s=function(t,n){var r=f(t,n),o=c(t);if(null===o)return r;var u=s(o,n);return u.length?r.length?i(new e(r.concat(u))):u:r};o.exp({getMetadataKeys:function(t){return s(u(t),arguments.length<2?void 0:a(arguments[1]))}})},function(t,n,r){var e=r(54),i=r(2),o=r(32),u=e.has,c=e.get,f=e.key,a=function(t,n,r){if(u(t,n,r))return c(t,n,r);var e=o(n);return null!==e?a(t,e,r):void 0};e.exp({getMetadata:function(t,n){return a(t,i(n),arguments.length<3?void 0:f(arguments[2]))}})},function(t,n,r){var e=r(54),i=r(2),o=e.keys,u=e.key;e.exp({getOwnMetadataKeys:function(t){
return o(i(t),arguments.length<2?void 0:u(arguments[1]))}})},function(t,n,r){var e=r(54),i=r(2),o=e.get,u=e.key;e.exp({getOwnMetadata:function(t,n){return o(t,i(n),arguments.length<3?void 0:u(arguments[2]))}})},function(t,n,r){var e=r(54),i=r(2),o=r(32),u=e.has,c=e.key,f=function(t,n,r){if(u(t,n,r))return!0;var e=o(n);return null!==e&&f(t,e,r)};e.exp({hasMetadata:function(t,n){return f(t,i(n),arguments.length<3?void 0:c(arguments[2]))}})},function(t,n,r){var e=r(54),i=r(2),o=e.has,u=e.key;e.exp({hasOwnMetadata:function(t,n){return o(t,i(n),arguments.length<3?void 0:u(arguments[2]))}})},function(t,n,r){var e=r(54),i=r(2),o=r(26),u=e.key,c=e.set;e.exp({metadata:function(t,n){return function(r,e){c(t,n,(void 0!==e?i:o)(r),u(e))}}})},function(t,n,r){var e=r(1);e(e.P+e.R,"Set",{toJSON:r(165)("Set")})},function(t,n,r){"use strict";var e=r(1),i=r(147)(!0);e(e.P,"String",{at:function(t){return i(this,t)}})},function(t,n,r){"use strict";var e=r(1),i=r(46),o=r(16),u=r(122),c=r(120),f=RegExp.prototype,a=function(t,n){this._r=t,this._s=n};r(139)(a,"RegExp String",function(){var t=this._r.exec(this._s);return{value:t,done:null===t}}),e(e.P,"String",{matchAll:function(t){if(i(this),!u(t))throw TypeError(t+" is not a regexp!");var n=String(this),r="flags"in f?String(t.flags):c.call(t),e=new RegExp(t.source,~r.indexOf("g")?r:"g"+r);return e.lastIndex=o(t.lastIndex),new a(e,n)}})},function(t,n,r){"use strict";var e=r(1),i=r(181);e(e.P,"String",{padEnd:function(t){return i(this,t,arguments.length>1?arguments[1]:void 0,!1)}})},function(t,n,r){"use strict";var e=r(1),i=r(181);e(e.P,"String",{padStart:function(t){return i(this,t,arguments.length>1?arguments[1]:void 0,!0)}})},function(t,n,r){"use strict";r(82)("trimLeft",function(t){return function(){return t(this,1)}},"trimStart")},function(t,n,r){"use strict";r(82)("trimRight",function(t){return function(){return t(this,2)}},"trimEnd")},function(t,n,r){r(153)("asyncIterator")},function(t,n,r){r(153)("observable")},function(t,n,r){var e=r(1);e(e.S,"System",{global:r(3)})},function(t,n,r){for(var e=r(155),i=r(28),o=r(3),u=r(27),c=r(80),f=r(7),a=f("iterator"),s=f("toStringTag"),l=c.Array,h=["NodeList","DOMTokenList","MediaList","StyleSheetList","CSSRuleList"],v=0;v<5;v++){var p,d=h[v],y=o[d],g=y&&y.prototype;if(g){g[a]||u(g,a,l),g[s]||u(g,s,d),c[d]=l;for(p in e)g[p]||i(g,p,e[p],!0)}}},function(t,n,r){var e=r(1),i=r(151);e(e.G+e.B,{setImmediate:i.set,clearImmediate:i.clear})},function(t,n,r){var e=r(3),i=r(1),o=r(121),u=r(207),c=e.navigator,f=!!c&&/MSIE .\./.test(c.userAgent),a=function(t){return f?function(n,r){return t(o(u,[].slice.call(arguments,2),"function"==typeof n?n:Function(n)),r)}:t};i(i.G+i.B+i.F*f,{setTimeout:a(e.setTimeout),setInterval:a(e.setInterval)})},function(t,n,r){r(330),r(269),r(271),r(270),r(273),r(275),r(280),r(274),r(272),r(282),r(281),r(277),r(278),r(276),r(268),r(279),r(283),r(284),r(236),r(238),r(237),r(286),r(285),r(256),r(266),r(267),r(257),r(258),r(259),r(260),r(261),r(262),r(263),r(264),r(265),r(239),r(240),r(241),r(242),r(243),r(244),r(245),r(246),r(247),r(248),r(249),r(250),r(251),r(252),r(253),r(254),r(255),r(317),r(322),r(329),r(320),r(312),r(313),r(318),r(323),r(325),r(308),r(309),r(310),r(311),r(314),r(315),r(316),r(319),r(321),r(324),r(326),r(327),r(328),r(231),r(233),r(232),r(235),r(234),r(220),r(218),r(224),r(221),r(227),r(229),r(217),r(223),r(214),r(228),r(212),r(226),r(225),r(219),r(222),r(211),r(213),r(216),r(215),r(230),r(155),r(302),r(307),r(184),r(303),r(304),r(305),r(306),r(287),r(183),r(185),r(186),r(342),r(331),r(332),r(337),r(340),r(341),r(335),r(338),r(336),r(339),r(333),r(334),r(288),r(289),r(290),r(291),r(292),r(295),r(293),r(294),r(296),r(297),r(298),r(299),r(301),r(300),r(343),r(369),r(372),r(371),r(373),r(374),r(370),r(375),r(376),r(354),r(357),r(353),r(351),r(352),r(355),r(356),r(346),r(368),r(377),r(345),r(347),r(349),r(348),r(350),r(359),r(360),r(362),r(361),r(364),r(363),r(365),r(366),r(367),r(344),r(358),r(380),r(379),r(378),t.exports=r(52)},function(t,n){function r(t,n){if("string"==typeof n)return t.insertAdjacentHTML("afterend",n);var r=t.nextSibling;return r?t.parentNode.insertBefore(n,r):t.parentNode.appendChild(n)}t.exports=r},,,,,,,,,function(t,n,r){(function(n,r){!function(n){"use strict";function e(t,n,r,e){var i=n&&n.prototype instanceof o?n:o,u=Object.create(i.prototype),c=new p(e||[]);return u._invoke=s(t,r,c),u}function i(t,n,r){try{return{type:"normal",arg:t.call(n,r)}}catch(t){return{type:"throw",arg:t}}}function o(){}function u(){}function c(){}function f(t){["next","throw","return"].forEach(function(n){t[n]=function(t){return this._invoke(n,t)}})}function a(t){function n(r,e,o,u){var c=i(t[r],t,e);if("throw"!==c.type){var f=c.arg,a=f.value;return a&&"object"==typeof a&&m.call(a,"__await")?Promise.resolve(a.__await).then(function(t){n("next",t,o,u)},function(t){n("throw",t,o,u)}):Promise.resolve(a).then(function(t){f.value=t,o(f)},u)}u(c.arg)}function e(t,r){function e(){return new Promise(function(e,i){n(t,r,e,i)})}return o=o?o.then(e,e):e()}"object"==typeof r&&r.domain&&(n=r.domain.bind(n));var o;this._invoke=e}function s(t,n,r){var e=P;return function(o,u){if(e===F)throw new Error("Generator is already running");if(e===M){if("throw"===o)throw u;return y()}for(r.method=o,r.arg=u;;){var c=r.delegate;if(c){var f=l(c,r);if(f){if(f===A)continue;return f}}if("next"===r.method)r.sent=r._sent=r.arg;else if("throw"===r.method){if(e===P)throw e=M,r.arg;r.dispatchException(r.arg)}else"return"===r.method&&r.abrupt("return",r.arg);e=F;var a=i(t,n,r);if("normal"===a.type){if(e=r.done?M:j,a.arg===A)continue;return{value:a.arg,done:r.done}}"throw"===a.type&&(e=M,r.method="throw",r.arg=a.arg)}}}function l(t,n){var r=t.iterator[n.method];if(r===g){if(n.delegate=null,"throw"===n.method){if(t.iterator.return&&(n.method="return",n.arg=g,l(t,n),"throw"===n.method))return A;n.method="throw",n.arg=new TypeError("The iterator does not provide a 'throw' method")}return A}var e=i(r,t.iterator,n.arg);if("throw"===e.type)return n.method="throw",n.arg=e.arg,n.delegate=null,A;var o=e.arg;return o?o.done?(n[t.resultName]=o.value,n.next=t.nextLoc,"return"!==n.method&&(n.method="next",n.arg=g),n.delegate=null,A):o:(n.method="throw",n.arg=new TypeError("iterator result is not an object"),n.delegate=null,A)}function h(t){var n={tryLoc:t[0]};1 in t&&(n.catchLoc=t[1]),2 in t&&(n.finallyLoc=t[2],n.afterLoc=t[3]),this.tryEntries.push(n)}function v(t){var n=t.completion||{};n.type="normal",delete n.arg,t.completion=n}function p(t){this.tryEntries=[{tryLoc:"root"}],t.forEach(h,this),this.reset(!0)}function d(t){if(t){var n=t[w];if(n)return n.call(t);if("function"==typeof t.next)return t;if(!isNaN(t.length)){var r=-1,e=function n(){for(;++r<t.length;)if(m.call(t,r))return n.value=t[r],n.done=!1,n;return n.value=g,n.done=!0,n};return e.next=e}}return{next:y}}function y(){return{value:g,done:!0}}var g,b=Object.prototype,m=b.hasOwnProperty,x="function"==typeof Symbol?Symbol:{},w=x.iterator||"@@iterator",S=x.asyncIterator||"@@asyncIterator",_=x.toStringTag||"@@toStringTag",O="object"==typeof t,E=n.regeneratorRuntime;if(E)return void(O&&(t.exports=E));E=n.regeneratorRuntime=O?t.exports:{},E.wrap=e;var P="suspendedStart",j="suspendedYield",F="executing",M="completed",A={},N={};N[w]=function(){return this};var T=Object.getPrototypeOf,I=T&&T(T(d([])));I&&I!==b&&m.call(I,w)&&(N=I);var k=c.prototype=o.prototype=Object.create(N);u.prototype=k.constructor=c,c.constructor=u,c[_]=u.displayName="GeneratorFunction",E.isGeneratorFunction=function(t){var n="function"==typeof t&&t.constructor;return!!n&&(n===u||"GeneratorFunction"===(n.displayName||n.name))},E.mark=function(t){return Object.setPrototypeOf?Object.setPrototypeOf(t,c):(t.__proto__=c,_ in t||(t[_]="GeneratorFunction")),t.prototype=Object.create(k),t},E.awrap=function(t){return{__await:t}},f(a.prototype),a.prototype[S]=function(){return this},E.AsyncIterator=a,E.async=function(t,n,r,i){var o=new a(e(t,n,r,i));return E.isGeneratorFunction(n)?o:o.next().then(function(t){return t.done?t.value:o.next()})},f(k),k[_]="Generator",k.toString=function(){return"[object Generator]"},E.keys=function(t){var n=[];for(var r in t)n.push(r);return n.reverse(),function r(){for(;n.length;){var e=n.pop();if(e in t)return r.value=e,r.done=!1,r}return r.done=!0,r}},E.values=d,p.prototype={constructor:p,reset:function(t){if(this.prev=0,this.next=0,this.sent=this._sent=g,this.done=!1,this.delegate=null,this.method="next",this.arg=g,this.tryEntries.forEach(v),!t)for(var n in this)"t"===n.charAt(0)&&m.call(this,n)&&!isNaN(+n.slice(1))&&(this[n]=g)},stop:function(){this.done=!0;var t=this.tryEntries[0],n=t.completion;if("throw"===n.type)throw n.arg;return this.rval},dispatchException:function(t){function n(n,e){return o.type="throw",o.arg=t,r.next=n,e&&(r.method="next",r.arg=g),!!e}if(this.done)throw t;for(var r=this,e=this.tryEntries.length-1;e>=0;--e){var i=this.tryEntries[e],o=i.completion;if("root"===i.tryLoc)return n("end");if(i.tryLoc<=this.prev){var u=m.call(i,"catchLoc"),c=m.call(i,"finallyLoc");if(u&&c){if(this.prev<i.catchLoc)return n(i.catchLoc,!0);if(this.prev<i.finallyLoc)return n(i.finallyLoc)}else if(u){if(this.prev<i.catchLoc)return n(i.catchLoc,!0)}else{if(!c)throw new Error("try statement without catch or finally");if(this.prev<i.finallyLoc)return n(i.finallyLoc)}}}},abrupt:function(t,n){for(var r=this.tryEntries.length-1;r>=0;--r){var e=this.tryEntries[r];if(e.tryLoc<=this.prev&&m.call(e,"finallyLoc")&&this.prev<e.finallyLoc){var i=e;break}}i&&("break"===t||"continue"===t)&&i.tryLoc<=n&&n<=i.finallyLoc&&(i=null);var o=i?i.completion:{};return o.type=t,o.arg=n,i?(this.method="next",this.next=i.finallyLoc,A):this.complete(o)},complete:function(t,n){if("throw"===t.type)throw t.arg;return"break"===t.type||"continue"===t.type?this.next=t.arg:"return"===t.type?(this.rval=this.arg=t.arg,this.method="return",this.next="end"):"normal"===t.type&&n&&(this.next=n),A},finish:function(t){for(var n=this.tryEntries.length-1;n>=0;--n){var r=this.tryEntries[n];if(r.finallyLoc===t)return this.complete(r.completion,r.afterLoc),v(r),A}},catch:function(t){for(var n=this.tryEntries.length-1;n>=0;--n){var r=this.tryEntries[n];if(r.tryLoc===t){var e=r.completion;if("throw"===e.type){var i=e.arg;v(r)}return i}}throw new Error("illegal catch attempt")},delegateYield:function(t,n,r){return this.delegate={iterator:d(t),resultName:n,nextLoc:r},"next"===this.method&&(this.arg=g),A}}}("object"==typeof n?n:"object"==typeof window?window:"object"==typeof self?self:this)}).call(n,function(){return this}(),r(158))}])</script><script src="/./main.0cf68a.js"></script><script>!function(){!function(e){var t=document.createElement("script");document.getElementsByTagName("body")[0].appendChild(t),t.setAttribute("src",e)}("/slider.e37972.js")}()</script>


    
<div class="tools-col" q-class="show:isShow,hide:isShow|isFalse" q-on="click:stop(e)">
  <div class="tools-nav header-menu">
    
    
      
      
      
    
      
      
      
    
      
      
      
    
    

    <ul style="width: 70%">
    
    
      
      <li style="width: 33.333333333333336%" q-on="click: openSlider(e, 'innerArchive')"><a href="javascript:void(0)" q-class="active:innerArchive">所有文章</a></li>
      
        
      
      <li style="width: 33.333333333333336%" q-on="click: openSlider(e, 'friends')"><a href="javascript:void(0)" q-class="active:friends">友链</a></li>
      
        
      
      <li style="width: 33.333333333333336%" q-on="click: openSlider(e, 'aboutme')"><a href="javascript:void(0)" q-class="active:aboutme">关于我</a></li>
      
        
    </ul>
  </div>
  <div class="tools-wrap">
    
    	<section class="tools-section tools-section-all" q-show="innerArchive">
        <div class="search-wrap">
          <input class="search-ipt" q-model="search" type="text" placeholder="find something…">
          <i class="icon-search icon" q-show="search|isEmptyStr"></i>
          <i class="icon-close icon" q-show="search|isNotEmptyStr" q-on="click:clearChose(e)"></i>
        </div>
        <div class="widget tagcloud search-tag">
          <p class="search-tag-wording">tag:</p>
          <label class="search-switch">
            <input type="checkbox" q-on="click:toggleTag(e)" q-attr="checked:showTags">
          </label>
          <ul class="article-tag-list" q-show="showTags">
            
            <div class="clearfix"></div>
          </ul>
        </div>
        <ul class="search-ul">
          <p q-show="jsonFail" style="padding: 20px; font-size: 12px;">
            缺失模块。<br/>1、请确保node版本大于6.2<br/>2、在博客根目录（注意不是yilia根目录）执行以下命令：<br/> npm i hexo-generator-json-content --save<br/><br/>
            3、在根目录_config.yml里添加配置：
<pre style="font-size: 12px;" q-show="jsonFail">
  jsonContent:
    meta: false
    pages: false
    posts:
      title: true
      date: true
      path: true
      text: false
      raw: false
      content: false
      slug: false
      updated: false
      comments: false
      link: false
      permalink: false
      excerpt: false
      categories: false
      tags: true
</pre>
          </p>
          <li class="search-li" q-repeat="items" q-show="isShow">
            <a q-attr="href:path|urlformat" class="search-title"><i class="icon-quo-left icon"></i><span q-text="title"></span></a>
            <p class="search-time">
              <i class="icon-calendar icon"></i>
              <span q-text="date|dateformat"></span>
            </p>
            <p class="search-tag">
              <i class="icon-price-tags icon"></i>
              <span q-repeat="tags" q-on="click:choseTag(e, name)" q-text="name|tagformat"></span>
            </p>
          </li>
        </ul>
    	</section>
    

    
    	<section class="tools-section tools-section-friends" q-show="friends">
  		
        <ul class="search-ul">
          
            <li class="search-li">
              <a href="http://124.222.76.228/" target="_blank" class="search-title"><i class="icon-quo-left icon"></i>友情链接1</a>
            </li>
          
            <li class="search-li">
              <a href="http://localhost:4000/" target="_blank" class="search-title"><i class="icon-quo-left icon"></i>友情链接2</a>
            </li>
          
            <li class="search-li">
              <a href="http://localhost:4000/" target="_blank" class="search-title"><i class="icon-quo-left icon"></i>友情链接3</a>
            </li>
          
            <li class="search-li">
              <a href="http://localhost:4000/" target="_blank" class="search-title"><i class="icon-quo-left icon"></i>友情链接4</a>
            </li>
          
            <li class="search-li">
              <a href="http://localhost:4000/" target="_blank" class="search-title"><i class="icon-quo-left icon"></i>友情链接5</a>
            </li>
          
            <li class="search-li">
              <a href="http://localhost:4000/" target="_blank" class="search-title"><i class="icon-quo-left icon"></i>友情链接6</a>
            </li>
          
        </ul>
  		
    	</section>
    

    
    	<section class="tools-section tools-section-me" q-show="aboutme">
  	  	
  	  		<div class="aboutme-wrap" id="js-aboutme">QQ   :930818716&lt;br&gt;邮箱:liyuanzhe@mail.ustc.edu.cn&lt;br&gt;&lt;br&gt;很惭愧&lt;br&gt;&lt;br&gt;只做了一点微小的工作&lt;br&gt;谢谢大家</div>
  	  	
    	</section>
    
  </div>
  
</div>
    <!-- Root element of PhotoSwipe. Must have class pswp. -->
<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">

    <!-- Background of PhotoSwipe. 
         It's a separate element as animating opacity is faster than rgba(). -->
    <div class="pswp__bg"></div>

    <!-- Slides wrapper with overflow:hidden. -->
    <div class="pswp__scroll-wrap">

        <!-- Container that holds slides. 
            PhotoSwipe keeps only 3 of them in the DOM to save memory.
            Don't modify these 3 pswp__item elements, data is added later on. -->
        <div class="pswp__container">
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
        </div>

        <!-- Default (PhotoSwipeUI_Default) interface on top of sliding area. Can be changed. -->
        <div class="pswp__ui pswp__ui--hidden">

            <div class="pswp__top-bar">

                <!--  Controls are self-explanatory. Order can be changed. -->

                <div class="pswp__counter"></div>

                <button class="pswp__button pswp__button--close" title="Close (Esc)"></button>

                <button class="pswp__button pswp__button--share" style="display:none" title="Share"></button>

                <button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>

                <button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>

                <!-- Preloader demo http://codepen.io/dimsemenov/pen/yyBWoR -->
                <!-- element will get class pswp__preloader--active when preloader is running -->
                <div class="pswp__preloader">
                    <div class="pswp__preloader__icn">
                      <div class="pswp__preloader__cut">
                        <div class="pswp__preloader__donut"></div>
                      </div>
                    </div>
                </div>
            </div>

            <div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
                <div class="pswp__share-tooltip"></div> 
            </div>

            <button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
            </button>

            <button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
            </button>

            <div class="pswp__caption">
                <div class="pswp__caption__center"></div>
            </div>

        </div>

    </div>

</div>
  </div>
</body>
</html>